diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 5cd51186e..dfae4e089 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -3,9 +3,9 @@ name: Build, lint and test on: # Triggers the workflow on push or pull request events but only for the main branch and release branches push: - branches: [main, release**] + branches: [main, release**, feature/**] pull_request: - branches: [main, release**] + branches: [main, release**, feature/**] # Allows us to run the workflow manually from the Actions tab workflow_dispatch: diff --git a/packages/browser-wallet/.storybook/main.js b/packages/browser-wallet/.storybook/main.js index 5ed1b0ac6..c873a76d3 100644 --- a/packages/browser-wallet/.storybook/main.js +++ b/packages/browser-wallet/.storybook/main.js @@ -38,4 +38,10 @@ module.exports = { return config; }, + babel: async (options) => { + return { + ...options, + plugins: options.plugins.filter((x) => !(typeof x === 'string' && x.includes('plugin-transform-classes'))), + }; + }, }; diff --git a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialCard.stories.tsx b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialCard.stories.tsx index a069831b4..8920d6d87 100644 --- a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialCard.stories.tsx +++ b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialCard.stories.tsx @@ -62,7 +62,7 @@ const metadata: VerifiableCredentialMetadata = { url: 'https://img.logoipsum.com/298.svg', hash: '1c74f7eb1b3343a5834e60e9a8fce277f2c7553112accd42e63fae7a09e0caf8', }, - background_color: '#003d73', + backgroundColor: '#003d73', image: { url: 'https://picsum.photos/327/120', }, diff --git a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialCard.tsx b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialCard.tsx index f13449d85..30765062a 100644 --- a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialCard.tsx +++ b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialCard.tsx @@ -50,7 +50,7 @@ function ClickableVerifiableCredential({ children, onClick, metadata, className return (
{ if (e.key === 'Enter') { @@ -65,10 +65,7 @@ function ClickableVerifiableCredential({ children, onClick, metadata, className ); } return ( -
+
{children}
); diff --git a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialDetails.tsx b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialDetails.tsx index 493e34928..45905a334 100644 --- a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialDetails.tsx +++ b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialDetails.tsx @@ -51,10 +51,7 @@ function VerifiableCredentialExtraDetails({ return (
-
+
(showExtraDetails ? setShowExtraDetails(false) : backButtonOnClick()), - }} + onBackButtonClick={() => (showExtraDetails ? setShowExtraDetails(false) : backButtonOnClick())} menuButton={menuButton} /> {showExtraDetails && ( diff --git a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialHooks.tsx b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialHooks.tsx index 31de42de6..3f3b00988 100644 --- a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialHooks.tsx +++ b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialHooks.tsx @@ -27,9 +27,9 @@ export function useCredentialStatus(credential: VerifiableCredential) { const client = useAtomValue(grpcClientAtom); useEffect(() => { - getVerifiableCredentialStatus(client, credential.id).then((credentialStatus) => { - setStatus(credentialStatus); - }); + getVerifiableCredentialStatus(client, credential.id) + .then(setStatus) + .catch(() => setStatus(VerifiableCredentialStatus.Pending)); }, [credential.id, client]); return status; diff --git a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialList.tsx b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialList.tsx index a302bb68d..d2e0e9bcd 100644 --- a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialList.tsx +++ b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialList.tsx @@ -36,7 +36,7 @@ function NoVerifiableCredentials() { const { t } = useTranslation('verifiableCredential'); return ( <> - +

You do not have any verifiable credentials in your wallet.

@@ -137,7 +137,7 @@ export default function VerifiableCredentialList() { return ( <> - +
{verifiableCredentials.value.map((credential) => { return ( diff --git a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialStatus.tsx b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialStatus.tsx index 7155220f2..ce9ad400a 100644 --- a/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialStatus.tsx +++ b/packages/browser-wallet/src/popup/pages/VerifiableCredential/VerifiableCredentialStatus.tsx @@ -4,24 +4,36 @@ import RevokedIcon from '@assets/svg/revoked.svg'; import ActiveIcon from '@assets/svg/verified.svg'; import ExpiredIcon from '@assets/svg/block.svg'; import PendingIcon from '@assets/svg/pending.svg'; +import { useTranslation } from 'react-i18next'; /** * Component for displaying the status of a verifiable credential. */ export default function StatusIcon({ status }: { status: VerifiableCredentialStatus }) { + const { t } = useTranslation('verifiableCredential', { keyPrefix: 'status' }); + let icon = null; + let text = ''; switch (status) { case VerifiableCredentialStatus.Active: icon = ; + text = t('Active'); break; case VerifiableCredentialStatus.Revoked: icon = ; + text = t('Revoked'); break; case VerifiableCredentialStatus.Expired: icon = ; + text = t('Expired'); break; case VerifiableCredentialStatus.NotActivated: icon = ; + text = t('Pending'); + break; + case VerifiableCredentialStatus.Pending: + icon = ; + text = t('Pending'); break; default: icon = null; @@ -30,7 +42,7 @@ export default function StatusIcon({ status }: { status: VerifiableCredentialSta return (
- {VerifiableCredentialStatus[status]} + {text} {icon}
); diff --git a/packages/browser-wallet/src/popup/pages/VerifiableCredential/i18n/da.ts b/packages/browser-wallet/src/popup/pages/VerifiableCredential/i18n/da.ts index 75341d4ed..59d8f2435 100644 --- a/packages/browser-wallet/src/popup/pages/VerifiableCredential/i18n/da.ts +++ b/packages/browser-wallet/src/popup/pages/VerifiableCredential/i18n/da.ts @@ -14,6 +14,13 @@ const t: typeof en = { validFrom: 'Gyldig fra', validUntil: 'Gyldig indtil', }, + status: { + Active: 'Aktiv', + Revoked: 'Ophævet', + Expired: 'Udløbet', + NotActivated: 'Ikke aktiveret', + Pending: 'Afventer', + }, }; export default t; diff --git a/packages/browser-wallet/src/popup/pages/VerifiableCredential/i18n/en.ts b/packages/browser-wallet/src/popup/pages/VerifiableCredential/i18n/en.ts index aca3d8fd3..f88d5d750 100644 --- a/packages/browser-wallet/src/popup/pages/VerifiableCredential/i18n/en.ts +++ b/packages/browser-wallet/src/popup/pages/VerifiableCredential/i18n/en.ts @@ -12,6 +12,13 @@ const t = { validFrom: 'Valid from', validUntil: 'Valid until', }, + status: { + Active: 'Active', + Revoked: 'Revoked', + Expired: 'Expired', + NotActivated: 'Not activated', + Pending: 'Pending', + }, }; export default t; diff --git a/packages/browser-wallet/src/popup/shared/Topbar/Topbar.stories.tsx b/packages/browser-wallet/src/popup/shared/Topbar/Topbar.stories.tsx index 4afbd4e94..34aba8de9 100644 --- a/packages/browser-wallet/src/popup/shared/Topbar/Topbar.stories.tsx +++ b/packages/browser-wallet/src/popup/shared/Topbar/Topbar.stories.tsx @@ -22,23 +22,25 @@ const Template: ComponentStory = (args) => { export const WithBackButton = Template.bind({}); WithBackButton.args = { title: 'Page Navigation Title', + onBackButtonClick: () => {}, }; export const WithoutBackButton = Template.bind({}); WithoutBackButton.args = { title: 'Page Navigation Title', - backButton: { show: false }, + onBackButtonClick: undefined, }; export const WithMoreMenuButton = Template.bind({}); WithMoreMenuButton.args = { title: 'Page Navigation Title', - backButton: { show: false }, + onBackButtonClick: undefined, menuButton: { type: ButtonTypes.More, items: [{ title: 'Revoke', icon:
Test
}] }, }; export const WithBackAndMoreMenuButton = Template.bind({}); WithBackAndMoreMenuButton.args = { title: 'Page Navigation Title', + onBackButtonClick: () => {}, menuButton: { type: ButtonTypes.More, items: [{ title: 'Revoke', icon:
Test
}] }, }; diff --git a/packages/browser-wallet/src/popup/shared/Topbar/Topbar.tsx b/packages/browser-wallet/src/popup/shared/Topbar/Topbar.tsx index 416c3c2e2..a7fb34168 100644 --- a/packages/browser-wallet/src/popup/shared/Topbar/Topbar.tsx +++ b/packages/browser-wallet/src/popup/shared/Topbar/Topbar.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import BackIcon from '@assets/svg/back-icon.svg'; import MoreIcon from '@assets/svg/more.svg'; -import { useNavigate } from 'react-router-dom'; import clsx from 'clsx'; import Button from '../Button'; import PopupMenu, { PopupMenuItem } from '../PopupMenu/PopupMenu'; @@ -10,17 +9,6 @@ export enum ButtonTypes { More, } -interface NoBackButton { - show: false; -} - -interface ShowBackButton { - show: true; - onClick: () => void; -} - -type BackButton = ShowBackButton | NoBackButton; - interface MoreMenuButton { type: ButtonTypes.More; items: PopupMenuItem[]; @@ -30,28 +18,18 @@ export type MenuButton = MoreMenuButton; interface TopbarProps { title: string; - backButton?: BackButton; + onBackButtonClick?: () => void; menuButton?: MenuButton; } -export default function Topbar({ - title, - backButton = { - show: true, - onClick: () => { - const nav = useNavigate(); - return nav(-1); - }, - }, - menuButton, -}: TopbarProps) { +export default function Topbar({ title, onBackButtonClick, menuButton }: TopbarProps) { const [showPopupMenu, setShowPopupMenu] = useState(false); return (
- {backButton.show && ( - )} diff --git a/packages/browser-wallet/src/shared/storage/types.ts b/packages/browser-wallet/src/shared/storage/types.ts index 54b22ee75..9e00302e8 100644 --- a/packages/browser-wallet/src/shared/storage/types.ts +++ b/packages/browser-wallet/src/shared/storage/types.ts @@ -265,6 +265,10 @@ export enum VerifiableCredentialStatus { Revoked, Expired, NotActivated, + + // Pending is a local wallet state not reflected on chain. This is used + // when a credential is added to the wallet, but it is still not on chain. + Pending, } export type CredentialSubject = { diff --git a/packages/browser-wallet/src/shared/utils/verifiable-credential-helpers.ts b/packages/browser-wallet/src/shared/utils/verifiable-credential-helpers.ts index 700fb11a7..d961ce97d 100644 --- a/packages/browser-wallet/src/shared/utils/verifiable-credential-helpers.ts +++ b/packages/browser-wallet/src/shared/utils/verifiable-credential-helpers.ts @@ -141,11 +141,13 @@ export async function buildRevokeTransactionParameters( nonce: bigint, signingKey: string ) { + const fiveMinutesInMilliseconds = 5 * 60000; + const signatureExpirationTimestamp = BigInt(Date.now() + fiveMinutesInMilliseconds); const signingData: SigningData = { contractAddress: address, entryPoint: 'revokeCredentialHolder', nonce, - timestamp: BigInt(Date.now() + 5 * 60000), + timestamp: signatureExpirationTimestamp, }; const data: RevocationDataHolder = { @@ -240,7 +242,7 @@ export async function getVerifiableCredentialStatus(client: ConcordiumGRPCClient const contractAddress = getCredentialRegistryContractAddress(credentialId); const instanceInfo = await client.getInstanceInfo(contractAddress); if (instanceInfo === undefined) { - return undefined; + throw new Error('Given contract address was not a created instance'); } const result = await client.invokeContract({ @@ -464,7 +466,7 @@ const verifiableCredentialMetadataSchema = { VerifiableCredentialMetadata: { additionalProperties: false, properties: { - background_color: { + backgroundColor: { type: 'string', }, image: { @@ -483,7 +485,7 @@ const verifiableCredentialMetadataSchema = { type: 'string', }, }, - required: ['title', 'logo', 'background_color'], + required: ['title', 'logo', 'backgroundColor'], type: 'object', }, }, @@ -492,7 +494,7 @@ const verifiableCredentialMetadataSchema = { export interface VerifiableCredentialMetadata { title: string; logo: MetadataUrl; - background_color: string; + backgroundColor: string; image?: MetadataUrl; localization?: Record; } @@ -770,7 +772,7 @@ export async function getChangesToCredentialMetadata( let updateReceived = false; for (const updatedMetadata of upToDateCredentialMetadata) { - if (storedMetadata.value === undefined) { + if (Object.keys(updatedStoredMetadata).length === 0) { updatedStoredMetadata = { [updatedMetadata.url]: updatedMetadata.metadata, }; @@ -797,7 +799,7 @@ export async function getChangesToCredentialSchemas( let updateReceived = false; for (const updatedSchema of upToDateSchemas) { - if (storedSchemas === undefined) { + if (Object.keys(updatedSchemasInStorage).length === 0) { updatedSchemasInStorage = { [updatedSchema.$id]: updatedSchema, };