-
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.
veramo agent for vp & credential select popup
- Loading branch information
1 parent
0dc1021
commit c4c607a
Showing
6 changed files
with
5,171 additions
and
3,427 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
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; |
Oops, something went wrong.