Skip to content

Commit

Permalink
veramo agent for vp & credential select popup
Browse files Browse the repository at this point in the history
  • Loading branch information
jessevanmuijden committed Dec 29, 2024
1 parent 0dc1021 commit c4c607a
Show file tree
Hide file tree
Showing 6 changed files with 5,171 additions and 3,427 deletions.
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@veramo/core": "^6.0.0",
"@veramo/credential-w3c": "^6.0.0",
"@veramo/did-manager": "^6.0.0",
"@veramo/did-provider-key": "^6.0.0",
"@veramo/did-resolver": "^6.0.0",
"@veramo/key-manager": "^6.0.0",
"@veramo/kms-local": "^6.0.0",
"asn1js": "^3.0.5",
"autoprefixer": "^10.4.14",
"axios": "^1.4.0",
Expand Down
1 change: 0 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ function App() {
const hasCode = !hasCredentialOffer && queryParams.get('code');

const hasVerifiablePresentationRequest = !hasCode && queryParams.get('request_uri');

const hasAuthorizationRequest = !hasCredentialOffer && !hasCode && !hasVerifiablePresentationRequest;

const error = queryParams.get('error');
Expand Down
210 changes: 210 additions & 0 deletions src/components/Popups/SelectCredentialPopup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import React, { useEffect, useMemo, useState, useContext } from 'react';
import PopupLayout from './PopupLayout';
import { FaShare, FaRegCircle, FaCheckCircle, FaCommentDots } from 'react-icons/fa';
import { useTranslation, Trans } from 'react-i18next';
import CredentialImage from '../Credentials/CredentialImage';
import CredentialInfo from '../Credentials/CredentialInfo';
import Button from '../Buttons/Button';
import ContainerContext from '../../context/ContainerContext';
import useScreenType from '../../hooks/useScreenType';
import Slider from '../Shared/Slider';

const formatTitle = (title) => {
if (title) {
return title.replace(/([a-z])([A-Z])/g, '$1 $2');
} else {
return;
}
};

const StepBar = ({ totalSteps, currentStep, stepTitles }) => {

return (
<div className="flex items-center justify-center w-full my-4">
{Array.from({ length: totalSteps }, (_, index) => {
const isActive = index + 1 < currentStep;
const isCurrent = index + 1 === currentStep;
return (
<React.Fragment key={index}>
<div className="flex flex-col items-center">
<div
className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold ${isActive ? 'text-white bg-primary dark:bg-primary-light border-2 border-primary dark:border-primary-light' : isCurrent ? 'text-primary dark:text-white dark:bg-gray-700 border-2 border-primary dark:border-primary-light' : 'text-gray-400 border-2 border-gray-400 dark:border-gray-400'
}`}
>
{index + 1}
</div>
<p
className={`text-xs font-bold mt-1 ${isActive ? 'text-primary dark:text-primary-light' : isCurrent ? 'text-primary dark:text-white' : 'text-gray-400'} max-w-[60px] sm:max-w-[100px] text-center overflow-hidden whitespace-nowrap overflow-ellipsis`}
title={formatTitle(stepTitles[index])}
>
{formatTitle(stepTitles[index])}
</p>
</div>
{index < totalSteps - 1 && (
<div className="flex-auto h-[2px] bg-gray-400">
<div
className={`h-[2px] ${isActive ? 'bg-primary dark:bg-primary-light' : ''} transition-all duration-300`}
style={{ width: isActive ? '100%' : '0%' }}
></div>
</div>
)}
</React.Fragment>
);
})}
</div>
);
};

function SelectCredentialPopup({
isOpen,
setIsOpen,
onCredentialSelect,
credentialName,
verifierDomainName,
purpose,
selectableCredentials,
}) {
const [vcEntities, setVcEntities] = useState([]);
const { t } = useTranslation();
const [currentIndex, setCurrentIndex] = useState(0);
const [selectedCredential, setSelectedCredential] = useState(null);
const container = useContext(ContainerContext);
const screenType = useScreenType();
const [currentSlide, setCurrentSlide] = useState(1);

useEffect(() => {
const getData = async () => {
try {
const entities = await Promise.all(
selectableCredentials.map(async vcEntity => {
return container.credentialParserRegistry.parse(vcEntity.credential).then((c) => {
if ('error' in c) {
return;
}
return { ...vcEntity, friendlyName: c.credentialFriendlyName }
});
})
);

console.log(entities);

setVcEntities(entities);
} catch (error) {
console.error('Failed to parse credentials', error);
}
};

getData();
}, [
container.credentialParserRegistry,
selectableCredentials,
]);

const goToNextSelection = () => {
setCurrentIndex((i) => i + 1);
onCredentialSelect(selectableCredentials.find(c => c.credentialIdentifier === selectedCredential));
}

const goToPreviousSelection = () => {
if (currentIndex > 0) {
setCurrentIndex(currentIndex - 1);
}
};

const handleClick = (credentialIdentifier) => {
if (selectedCredential === credentialIdentifier) {
setSelectedCredential(null);
} else {
setSelectedCredential(credentialIdentifier);
}
};

const onClose = () => {
setIsOpen(false);
}

if (!isOpen) {
return null;
};

const renderSlideContent = (vcEntity) => (
<button
key={vcEntity.id}
className="relative rounded-xl transition-shadow shadow-md hover:shadow-xl cursor-pointer"
tabIndex={currentSlide !== vcEntities.indexOf(vcEntity) + 1 ? -1 : 0}
onClick={() => handleClick(vcEntity.credentialIdentifier)}
aria-label={`${vcEntity.friendlyName}`}
title={t('selectCredentialPopup.credentialSelectTitle', { friendlyName: vcEntity.friendlyName })}
>
<CredentialImage
vcEntityInstances={[vcEntity]}
key={vcEntity.credentialIdentifier}
credential={vcEntity.credential}
className="w-full object-cover rounded-xl"
showRibbon={currentSlide === vcEntities.indexOf(vcEntity) + 1}
/>

<div className={`absolute inset-0 rounded-xl transition-opacity bg-white/50 ${selectedCredential === vcEntity.credentialIdentifier ? 'opacity-0' : 'opacity-50'}`} />
<div className="absolute bottom-4 right-4 z-60">
{selectedCredential === vcEntity.credentialIdentifier ? (
<FaCheckCircle size={30} className="z-50 rounded-full bg-white text-primary dark:text-primary-light" />
) : (
<FaRegCircle size={30} className="z-50 rounded-full bg-white/50 text-primary dark:text-primary-light" />
)}
</div>
</button>
);

return (
<PopupLayout isOpen={isOpen} onClose={onClose} loading={false} fullScreen={screenType !== 'desktop'}>
<div className={`${screenType !== 'desktop' && 'pb-16'}`}>
<div>
<h2 className="text-lg font-bold mb-0 text-primary dark:text-white">
<FaCommentDots size={20} className="inline mr-1 mb-1" />
{verifierDomainName + ':'}
</h2>
<p className="text-gray-700 dark:text-white py-2 mb-2 xm:py-1 px-6 rounded-r-xl">{purpose}</p>
<h2 className="text-lg font-bold mb-5 text-primary dark:text-white">
<FaShare size={20} className="inline mr-1 mb-1" />
{t('selectCredentialPopup.title') + credentialName}
</h2>
</div>
<div className={`${screenType === 'tablet' ? 'px-28' : 'px-4 xl:px-16'}`}>
<Slider
items={vcEntities}
renderSlideContent={renderSlideContent}
onSlideChange={(currentIndex) => setCurrentSlide(currentIndex + 1)}
/>
</div>
{vcEntities[currentSlide - 1] && (
<div className={`flex flex-wrap justify-center flex flex-row justify-center items-center mb-2 ${screenType === 'desktop' && 'overflow-y-auto items-center custom-scrollbar max-h-[20vh]'} ${screenType === 'tablet' && 'px-24'}`}>
<CredentialInfo credential={vcEntities[currentSlide - 1].credential} mainClassName={"text-xs w-full"} />
</div>
)}
<div className={`flex justify-between pt-4 z-10 ${screenType !== 'desktop' && 'fixed bottom-0 left-0 right-0 bg-white dark:bg-gray-800 flex px-6 pb-6 flex shadow-2xl rounded-t-lg w-auto'}`}>
<Button
onClick={onClose}
variant="cancel"
className="mr-2"
>
{t('common.cancel')}
</Button>

<div className="flex gap-2">
<Button
onClick={goToNextSelection}
variant="primary"
disabled={!selectedCredential}
title={!selectedCredential ? t('selectCredentialPopup.nextButtonDisabledTitle') : ''}
>
{t('common.navItemSendCredentialsSimple')}
</Button>
</div>
</div>
</div>

</PopupLayout >
);
}

export default SelectCredentialPopup;
Loading

0 comments on commit c4c607a

Please sign in to comment.