Skip to content

Commit

Permalink
Merge branch 'revoke-credentials' of github.com:Concordium/concordium…
Browse files Browse the repository at this point in the history
…-browser-wallet into extra-details
  • Loading branch information
orhoj committed Aug 17, 2023
2 parents 494cb0e + dfbc0bd commit d1ee19c
Show file tree
Hide file tree
Showing 14 changed files with 66 additions and 59 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-lint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions packages/browser-wallet/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'))),
};
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function ClickableVerifiableCredential({ children, onClick, metadata, className
return (
<div
className={clsx('verifiable-credential verifiable-credential__clickable', className)}
style={{ backgroundColor: metadata.background_color }}
style={{ backgroundColor: metadata.backgroundColor }}
onClick={onClick}
onKeyDown={(e) => {
if (e.key === 'Enter') {
Expand All @@ -65,10 +65,7 @@ function ClickableVerifiableCredential({ children, onClick, metadata, className
);
}
return (
<div
className={clsx('verifiable-credential', className)}
style={{ backgroundColor: metadata.background_color }}
>
<div className={clsx('verifiable-credential', className)} style={{ backgroundColor: metadata.backgroundColor }}>
{children}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ function VerifiableCredentialExtraDetails({

return (
<div className="verifiable-credential-wrapper">
<div
className={`verifiable-credential ${className}`}
style={{ backgroundColor: metadata.background_color }}
>
<div className={`verifiable-credential ${className}`} style={{ backgroundColor: metadata.backgroundColor }}>
<VerifiableCredentialCardHeader credentialStatus={status} metadata={metadata} />
<div className="verifiable-credential__body-attributes">
<DisplayAttribute
Expand Down Expand Up @@ -140,8 +137,6 @@ export default function VerifiableCredentialDetails({
type: AccountTransactionType.Update,
};

// Override current router entry with stateful version
nav(pathname, { replace: true, state: true });
nav(`${absoluteRoutes.home.account.path}/${accountRoutes.confirmTransfer}`, {
state: confirmTransferState,
});
Expand Down Expand Up @@ -190,10 +185,7 @@ export default function VerifiableCredentialDetails({
<>
<Topbar
title={t('topbar.details')}
backButton={{
show: true,
onClick: () => (showExtraDetails ? setShowExtraDetails(false) : backButtonOnClick()),
}}
onBackButtonClick={() => (showExtraDetails ? setShowExtraDetails(false) : backButtonOnClick())}
menuButton={menuButton}
/>
{showExtraDetails && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function NoVerifiableCredentials() {
const { t } = useTranslation('verifiableCredential');
return (
<>
<Topbar title={t('topbar.list')} backButton={{ show: false }} />
<Topbar title={t('topbar.list')} />
<div className="verifiable-credential-wrapper">
<div className="flex-column align-center">
<p className="m-t-20 m-h-30">You do not have any verifiable credentials in your wallet.</p>
Expand Down Expand Up @@ -137,7 +137,7 @@ export default function VerifiableCredentialList() {

return (
<>
<Topbar title={t('topbar.list')} backButton={{ show: false }} />
<Topbar title={t('topbar.list')} />
<div className="verifiable-credential-wrapper">
{verifiableCredentials.value.map((credential) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <ActiveIcon />;
text = t('Active');
break;
case VerifiableCredentialStatus.Revoked:
icon = <RevokedIcon />;
text = t('Revoked');
break;
case VerifiableCredentialStatus.Expired:
icon = <ExpiredIcon />;
text = t('Expired');
break;
case VerifiableCredentialStatus.NotActivated:
icon = <PendingIcon />;
text = t('Pending');
break;
case VerifiableCredentialStatus.Pending:
icon = <PendingIcon />;
text = t('Pending');
break;
default:
icon = null;
Expand All @@ -30,7 +42,7 @@ export default function StatusIcon({ status }: { status: VerifiableCredentialSta

return (
<div className="verifiable-credential__header__status">
{VerifiableCredentialStatus[status]}
{text}
{icon}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,25 @@ const Template: ComponentStory<typeof Topbar> = (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: <div>Test</div> }] },
};

export const WithBackAndMoreMenuButton = Template.bind({});
WithBackAndMoreMenuButton.args = {
title: 'Page Navigation Title',
onBackButtonClick: () => {},
menuButton: { type: ButtonTypes.More, items: [{ title: 'Revoke', icon: <div>Test</div> }] },
};
30 changes: 4 additions & 26 deletions packages/browser-wallet/src/popup/shared/Topbar/Topbar.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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[];
Expand All @@ -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<boolean>(false);

return (
<div className="topbar">
<div className="topbar__icon-container">
{backButton.show && (
<Button clear onClick={backButton.onClick}>
{onBackButtonClick && (
<Button clear onClick={onBackButtonClick}>
<BackIcon className="topbar__icon-container__icon" />
</Button>
)}
Expand Down
4 changes: 4 additions & 0 deletions packages/browser-wallet/src/shared/storage/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -464,7 +466,7 @@ const verifiableCredentialMetadataSchema = {
VerifiableCredentialMetadata: {
additionalProperties: false,
properties: {
background_color: {
backgroundColor: {
type: 'string',
},
image: {
Expand All @@ -483,7 +485,7 @@ const verifiableCredentialMetadataSchema = {
type: 'string',
},
},
required: ['title', 'logo', 'background_color'],
required: ['title', 'logo', 'backgroundColor'],
type: 'object',
},
},
Expand All @@ -492,7 +494,7 @@ const verifiableCredentialMetadataSchema = {
export interface VerifiableCredentialMetadata {
title: string;
logo: MetadataUrl;
background_color: string;
backgroundColor: string;
image?: MetadataUrl;
localization?: Record<string, MetadataUrl>;
}
Expand Down Expand Up @@ -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,
};
Expand All @@ -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,
};
Expand Down

0 comments on commit d1ee19c

Please sign in to comment.