diff --git a/CHANGELOG.md b/CHANGELOG.md index ced78b20..718e406a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,6 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog], and this project adheres to [Semantic Versioning]. ## [Unreleased] - -## Unreleased ### Added - Initiated project diff --git a/package.json b/package.json index c54a8a60..08007a55 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "jdenticon": "^3.2.0", "lodash": "^4.17.21", "loglevel": "^1.8.1", + "material-ui-popup-state": "^5.0.10", "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/App.tsx b/src/App.tsx index 6be012df..82164089 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,7 @@ import { ToastsManager } from '@/contexts' import { ErrorHandler } from '@/helpers' import { useAuth, useViewportSizes, useWeb3Context } from '@/hooks' import { AppRoutes } from '@/routes' -import { useUiState, web3Store } from '@/store' +import { credentialsStore, useUiState, web3Store } from '@/store' import { createTheme } from '@/theme' const App: FC> = () => { @@ -23,6 +23,8 @@ const App: FC> = () => { if (isMetamaskInstalled && isSnapInstalled) { await connectProviders() + + await credentialsStore.load() } } catch (error) { ErrorHandler.processWithoutFeedback(error) diff --git a/src/api/clients/index.ts b/src/api/clients/index.ts index 1cfbd373..15b02acc 100644 --- a/src/api/clients/index.ts +++ b/src/api/clients/index.ts @@ -10,6 +10,6 @@ export const api = new JsonApiClient({ export let zkpSnap: SnapConnector export const initZkpSnap = async () => { - const snap = await enableSnap() + const snap = await enableSnap(...config.SNAP_V_PARAMS) zkpSnap = await snap.getConnector() } diff --git a/src/api/modules/orgs/helpers/org-groups-requests.ts b/src/api/modules/orgs/helpers/org-groups-requests.ts index e9c08538..059831d7 100644 --- a/src/api/modules/orgs/helpers/org-groups-requests.ts +++ b/src/api/modules/orgs/helpers/org-groups-requests.ts @@ -110,12 +110,16 @@ export const loadOrgGroupRequests = async (query?: OrgGroupRequestQueryParams) = return fakeLoadRequestsAll(query) } +// eslint-disable-next-line @typescript-eslint/no-unused-vars export const loadRequestsByUserDid = async (did: string): Promise => { - const { data } = await api.get( - `${ApiServicePaths.Orgs}/v1/users/${did}/requests`, - ) + // TODO: return once backend is ready + // const { data } = await api.get( + // `${ApiServicePaths.Orgs}/v1/users/${did}/requests`, + // ) + // + // return data - return data + return [] } export const loadOrgGroupRequestById = async (orgId: string, groupId: string, reqId: string) => { diff --git a/src/api/modules/zkp/helpers/credentials.ts b/src/api/modules/zkp/helpers/credentials.ts index 07eb6c7b..67dd4f73 100644 --- a/src/api/modules/zkp/helpers/credentials.ts +++ b/src/api/modules/zkp/helpers/credentials.ts @@ -1,10 +1,12 @@ import { fetcher } from '@distributedlab/fetcher' import { SaveCredentialsRequestParams, W3CCredential } from '@rarimo/rarime-connector' import omit from 'lodash/omit' +import startCase from 'lodash/startCase' import { api } from '@/api/clients' import { CredentialSubject, + IssuerDetails, JsonLdSchema, ParsedCredentialSchema, ParsedCredentialSchemaProperty, @@ -70,13 +72,38 @@ export const getTargetProperty = ( } export const getClaimIdFromVC = (credential: W3CCredential) => { + let claimId = '' + try { const claimIdUrl = new URL(credential.id) - const pathNameParts = claimIdUrl.pathname.split('/') - - return pathNameParts[pathNameParts.length - 1] + claimId = claimIdUrl.pathname.split('/').pop() ?? '' } catch (error) { - return credential.id + /* empty */ + } + + return claimId || credential.id +} + +export const getIssuerDetails = async (issuerDid: string): Promise => { + // TODO: This is a temporary solution, we need to get issuer details from the backend + return { + did: issuerDid, + name: 'Rarimo', + } +} + +export const getCredentialViewProperty = (vc: W3CCredential) => { + // TODO: This is a temporary solution, we need to get VC view property + return vc.credentialSubject.provider as string +} + +export const formatCredentialType = (vcType: string[]) => { + switch (vcType[1]) { + case 'IdentityProviders': { + return 'Proof of Human' + } + default: + return startCase(vcType[1]) } } diff --git a/src/api/modules/zkp/types/index.ts b/src/api/modules/zkp/types/index.ts index 4f8e9b2d..2ea61685 100644 --- a/src/api/modules/zkp/types/index.ts +++ b/src/api/modules/zkp/types/index.ts @@ -129,3 +129,8 @@ export type ParsedCredentialSchema = { type: string credSubjectProperties: ParsedCredentialSchemaProperty[] } + +export type IssuerDetails = { + did: string + name: string +} diff --git a/src/common/CredentialCard.tsx b/src/common/CredentialCard.tsx new file mode 100644 index 00000000..0a36f428 --- /dev/null +++ b/src/common/CredentialCard.tsx @@ -0,0 +1,120 @@ +import { alpha, Box, Divider, Stack, StackProps, Typography, useTheme } from '@mui/material' +import { W3CCredential } from '@rarimo/rarime-connector' +import startCase from 'lodash/startCase' + +import { formatCredentialType, getCredentialViewProperty, IssuerDetails } from '@/api/modules/zkp' +import { formatDateMY } from '@/helpers' +import { UiIcon } from '@/ui' + +function DotsDecoration({ ...rest }: StackProps) { + const { palette } = useTheme() + + const rowsCount = 3 + const maxDots = 5 + const betweenDotsSpacing = 2 + + // TODO: alpha(palette.common.white, ...) should be configured together with bg + return ( + + {Array.from({ length: rowsCount }, (v, i) => i).map(rowIdx => ( + + {Array.from({ length: maxDots - rowIdx }, (v, i) => i).map(boxIdx => ( + + ))} + + ))} + + ) +} + +type Props = StackProps & { + vc: W3CCredential + issuerDetails: IssuerDetails +} + +export default function CredentialCard({ vc, issuerDetails, ...rest }: Props) { + const { palette, spacing } = useTheme() + + return ( + + + + {/*TODO: define map for credential types*/} + + + + + + {formatCredentialType(vc.type)} + + + + {issuerDetails.name} + + + + + + + + {vc.expirationDate ? ( + <> + + {formatDateMY(vc.expirationDate)} + + ) : ( + + )} + + + + + + {startCase(getCredentialViewProperty(vc))} + + + + + ) +} diff --git a/src/common/index.ts b/src/common/index.ts index b4969ccc..87851315 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,4 +1,5 @@ export { default as AppNavbar } from './AppNavbar' +export { default as CredentialCard } from './CredentialCard' export { default as FillRequestForm } from './FillRequestForm' export { default as NoDataViewer } from './NoDataViewer' export { default as PageListFilters } from './PageListFilters' diff --git a/src/config.ts b/src/config.ts index cbe37e4c..b7dd93fa 100644 --- a/src/config.ts +++ b/src/config.ts @@ -16,6 +16,7 @@ export type Config = { DEFAULT_CHAIN: SupportedChains ROBOTORNOT_LINK: string SUPPORT_LINK: string + SNAP_V_PARAMS: string[] } const FALLBACK_DEFAULT_CHAIN = Object.entries(FALLBACK_SUPPORTED_CHAINS)[0][0] @@ -28,4 +29,5 @@ export const config: Config = { DEFAULT_CHAIN: import.meta.env.VITE_DEFAULT_CHAIN || FALLBACK_DEFAULT_CHAIN, ROBOTORNOT_LINK: 'https://robotornot.mainnet-beta.rarimo.com/', SUPPORT_LINK: 'https://rarime.com', + SNAP_V_PARAMS: [], // ['local:http://localhost:8081', '2.1.0-rc.2'], } diff --git a/src/enums/icons.ts b/src/enums/icons.ts index eab62a29..dcbc2d9a 100644 --- a/src/enums/icons.ts +++ b/src/enums/icons.ts @@ -1,9 +1,12 @@ import AccountCircleIcon from '@mui/icons-material/AccountCircle' import Add from '@mui/icons-material/Add' import AddPhotoAlternateOutlined from '@mui/icons-material/AddPhotoAlternateOutlined' +import AllInclusiveOutlinedIcon from '@mui/icons-material/AllInclusiveOutlined' import ArrowForward from '@mui/icons-material/ArrowForward' +import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined' import CheckIcon from '@mui/icons-material/Check' import ChevronLeft from '@mui/icons-material/ChevronLeft' +import ChevronRight from '@mui/icons-material/ChevronRight' import Close from '@mui/icons-material/Close' import ContentCopy from '@mui/icons-material/ContentCopy' import DarkModeOutlined from '@mui/icons-material/DarkModeOutlined' @@ -12,6 +15,7 @@ import DeleteOutlined from '@mui/icons-material/DeleteOutlined' import DragIndicator from '@mui/icons-material/DragIndicator' import DriveFileRenameOutlineOutlined from '@mui/icons-material/DriveFileRenameOutlineOutlined' import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline' +import FingerPrint from '@mui/icons-material/Fingerprint' import FolderOff from '@mui/icons-material/FolderOff' import InfoIcon from '@mui/icons-material/Info' import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined' @@ -21,6 +25,7 @@ import Layers from '@mui/icons-material/Layers' import LightModeOutlined from '@mui/icons-material/LightModeOutlined' import Link from '@mui/icons-material/Link' import Logout from '@mui/icons-material/Logout' +import MoreHoriz from '@mui/icons-material/MoreHoriz' import Notifications from '@mui/icons-material/Notifications' import OpenInNew from '@mui/icons-material/OpenInNew' import QrCode from '@mui/icons-material/QrCode' @@ -48,6 +53,7 @@ export const ICON_COMPONENTS = { arrowForward: ArrowForward, check: CheckIcon, chevronLeft: ChevronLeft, + chevronRight: ChevronRight, contentCopy: ContentCopy, close: Close, darkModeOutlined: DarkModeOutlined, @@ -73,4 +79,8 @@ export const ICON_COMPONENTS = { verified: Verified, warningAmber: WarningAmberIcon, work: Work, + fingerprint: FingerPrint, + calendarTodayOutlinedIcon: CalendarTodayOutlinedIcon, + allInclusiveOutlinedIcon: AllInclusiveOutlinedIcon, + moreHoriz: MoreHoriz, } diff --git a/src/enums/routes.ts b/src/enums/routes.ts index fd122f72..2724d697 100644 --- a/src/enums/routes.ts +++ b/src/enums/routes.ts @@ -27,5 +27,6 @@ export enum RoutePaths { Credentials = '/credentials', CredentialsList = '/credentials/list', + CredentialsId = '/credentials/:claimId', CredentialsRequests = '/credentials/requests', } diff --git a/src/helpers/format.ts b/src/helpers/format.ts index 49836ec3..06b6bbb0 100644 --- a/src/helpers/format.ts +++ b/src/helpers/format.ts @@ -1,5 +1,11 @@ +import { time } from '@distributedlab/tools' + const FORMATTED_DID_MAX_LENGTH = 12 export function formatDid(did: string) { return did.length > FORMATTED_DID_MAX_LENGTH ? did.slice(0, 8) + '...' + did.slice(-4) : did } + +export function formatDateMY(date: string) { + return time(date).format('MM / YYYY') +} diff --git a/src/helpers/store.ts b/src/helpers/store.ts index f95ce501..9d9bc942 100644 --- a/src/helpers/store.ts +++ b/src/helpers/store.ts @@ -1,10 +1,17 @@ -import { INTERNAL_Snapshot, proxy, subscribe, useSnapshot } from 'valtio' +import { proxy, subscribe, useSnapshot } from 'valtio' + +type CreateStoreOpts = { + isPersist: boolean +} export const createStore = ( storeName: string, initialState: S, actions: (state: S) => A, -): [Readonly & A, () => INTERNAL_Snapshot] => { + opts: CreateStoreOpts = { + isPersist: true, + }, +): [Readonly & A, () => S] => { const storageState = localStorage.getItem(storeName) let parsedStorageState: S = {} as S @@ -20,9 +27,11 @@ export const createStore = ( ...parsedStorageState, }) - subscribe(state, () => { - localStorage.setItem(storeName, JSON.stringify(state)) - }) + if (opts?.isPersist) { + subscribe(state, () => { + localStorage.setItem(storeName, JSON.stringify(state)) + }) + } - return [Object.assign(state, actions(state)), () => useSnapshot(state)] + return [Object.assign(state, actions(state)), () => useSnapshot(state) as S] } diff --git a/src/locales/resources/en.json b/src/locales/resources/en.json index 9477c22f..8f31d6c9 100644 --- a/src/locales/resources/en.json +++ b/src/locales/resources/en.json @@ -55,5 +55,8 @@ "org-list": { "title": "Organizations", "subtitle": "Manage your identity credentials and Soulbound Tokens (SBTs) easily from this app" + }, + "credentials-list": { + "title": "Credentials" } } diff --git a/src/pages/Credentials/contexts/CredentialsContextProvider.tsx b/src/pages/Credentials/contexts/CredentialsContextProvider.tsx index 8bccc29d..acea2621 100644 --- a/src/pages/Credentials/contexts/CredentialsContextProvider.tsx +++ b/src/pages/Credentials/contexts/CredentialsContextProvider.tsx @@ -1,4 +1,4 @@ -import { CircularProgress } from '@mui/material' +import { W3CCredential } from '@rarimo/rarime-connector' import { createContext, PropsWithChildren, useContext } from 'react' import { zkpSnap } from '@/api/clients' @@ -8,17 +8,22 @@ import { OrgGroupRequestWithClaims, OrgGroupVCMap, } from '@/api/modules/orgs' +import { getIssuerDetails, IssuerDetails } from '@/api/modules/zkp' import { useLoading } from '@/hooks' import { useIdentityState } from '@/store' type CredentialsContextValue = { + vcs: W3CCredential[] orgGroupRequests: OrgGroupRequestWithClaims[] groupedVCs: OrgGroupVCMap + issuersDetails: Record } const CredentialsContext = createContext({ + vcs: [], orgGroupRequests: [], groupedVCs: [], + issuersDetails: {}, }) export const CredentialsContextProvider = ({ children }: PropsWithChildren) => { @@ -29,16 +34,20 @@ export const CredentialsContextProvider = ({ children }: PropsWithChildren) => { const { userDid } = useIdentityState() const { - data: { orgGroupRequests, groupedVCs }, + data: { vcs, orgGroupRequests, groupedVCs, issuersDetails }, isLoading, isLoadingError, } = useLoading<{ + vcs: W3CCredential[] orgGroupRequests: OrgGroupRequestWithClaims[] groupedVCs: OrgGroupVCMap + issuersDetails: Record }>( { + vcs: [], orgGroupRequests: [], groupedVCs: [], + issuersDetails: {}, }, async () => { { @@ -49,7 +58,17 @@ export const CredentialsContextProvider = ({ children }: PropsWithChildren) => { const groupedVCs = groupVCsToOrgGroups(orgGroupRequests, vcs) - return { orgGroupRequests, groupedVCs } + const issuerDids = new Set(vcs.map(vc => vc.issuer)) + + const issuersDetails = ( + await Promise.all([...issuerDids].map(issuerDid => getIssuerDetails(issuerDid))) + ).reduce((acc, issuerDetails) => { + acc[issuerDetails.did] = issuerDetails + + return acc + }, {} as Record) + + return { vcs, orgGroupRequests, groupedVCs, issuersDetails } } }, { @@ -58,15 +77,17 @@ export const CredentialsContextProvider = ({ children }: PropsWithChildren) => { }, ) - if (isLoading) return + if (isLoading) return <> if (isLoadingError) return <> return ( {children} diff --git a/src/pages/Credentials/index.tsx b/src/pages/Credentials/index.tsx index 708f69ef..d3e8ebee 100644 --- a/src/pages/Credentials/index.tsx +++ b/src/pages/Credentials/index.tsx @@ -2,31 +2,30 @@ import { Navigate } from 'react-router-dom' import { RoutePaths } from '@/enums' import { useNestedRoutes } from '@/hooks' -import { CredentialsContextProvider } from '@/pages/Credentials/contexts' -import { CredentialsList, CredentialsRequests } from './pages' +import { CredentialsId, CredentialsList, CredentialsRequests } from './pages' export default function Credentials() { - return ( - - {useNestedRoutes(RoutePaths.Credentials, [ - { - index: true, - element: , - }, - { - path: RoutePaths.CredentialsList, - element: , - }, - { - path: RoutePaths.CredentialsRequests, - element: , - }, - { - path: '*', - element: , - }, - ])} - - ) + return useNestedRoutes(RoutePaths.Credentials, [ + { + index: true, + element: , + }, + { + path: RoutePaths.CredentialsList, + element: , + }, + { + path: RoutePaths.CredentialsRequests, + element: , + }, + { + path: RoutePaths.CredentialsId, + element: , + }, + { + path: '*', + element: , + }, + ]) } diff --git a/src/pages/Credentials/pages/CredentialsId/components/ActionButton.tsx b/src/pages/Credentials/pages/CredentialsId/components/ActionButton.tsx new file mode 100644 index 00000000..fc53fed8 --- /dev/null +++ b/src/pages/Credentials/pages/CredentialsId/components/ActionButton.tsx @@ -0,0 +1,67 @@ +import { Stack, Typography, useTheme } from '@mui/material' +import { ComponentProps } from 'react' + +import { Transitions } from '@/theme/constants' +import { UiButton, UiIcon } from '@/ui' + +export default function ActionButton({ + children, + iconProps, + ...rest +}: ComponentProps & { + iconProps: ComponentProps +}) { + const { palette, spacing } = useTheme() + + return ( + + + + + + + {children} + + + + ) +} diff --git a/src/pages/Credentials/pages/CredentialsId/components/index.ts b/src/pages/Credentials/pages/CredentialsId/components/index.ts new file mode 100644 index 00000000..d1b1093a --- /dev/null +++ b/src/pages/Credentials/pages/CredentialsId/components/index.ts @@ -0,0 +1 @@ +export { default as ActionButton } from './ActionButton' diff --git a/src/pages/Credentials/pages/CredentialsId/index.tsx b/src/pages/Credentials/pages/CredentialsId/index.tsx new file mode 100644 index 00000000..d4efc6cb --- /dev/null +++ b/src/pages/Credentials/pages/CredentialsId/index.tsx @@ -0,0 +1,174 @@ +import { Box, Button, Divider, Paper, Stack, Typography, useTheme } from '@mui/material' +import { useCallback, useMemo, useState } from 'react' +import { generatePath, NavLink, useParams } from 'react-router-dom' + +import { zkpSnap } from '@/api/clients' +import { getClaimIdFromVC } from '@/api/modules/zkp' +import { CredentialCard, NoDataViewer } from '@/common' +import { RoutePaths } from '@/enums' +import { ErrorHandler } from '@/helpers' +import { useCredentialsState } from '@/store' +import { UiIcon } from '@/ui' + +import { ActionButton } from './components' + +export default function CredentialsId() { + const [isPending, setIsPending] = useState(false) + + const { palette, spacing } = useTheme() + + const { claimId = '' } = useParams<{ claimId: string }>() + + const { vcs, issuersDetails } = useCredentialsState() + + const vc = useMemo(() => { + return vcs.find(vc => getClaimIdFromVC(vc) === claimId) + }, [claimId, vcs]) + + const vcIndex = useMemo(() => { + if (!vc) return -1 + + return vcs.indexOf(vc) + }, [vc, vcs]) + + const issuerDetails = useMemo(() => { + if (!vc) return + + return issuersDetails?.[vc.issuer] + }, [issuersDetails, vc]) + + const requestRemoveVC = useCallback(async () => { + if (!vc?.id) return + + setIsPending(true) + + try { + await zkpSnap.removeCredentials({ + ids: [vc.id], + }) + } catch (error) { + ErrorHandler.process(error) + } + + setIsPending(false) + }, [vc]) + + const isLastVC = useMemo(() => { + return vcIndex === vcs.length - 1 + }, [vcIndex, vcs.length]) + + const isFirstVC = useMemo(() => { + return vcIndex === 0 + }, [vcIndex]) + + const nextVCPath = useMemo(() => { + const nextIdx = vcIndex + 1 + + if (nextIdx >= vcs.length) return '' + + return generatePath(RoutePaths.CredentialsId, { + claimId: getClaimIdFromVC(vcs[nextIdx]), + }) + }, [vcIndex, vcs]) + + const prevVCPath = useMemo(() => { + const prevIdx = vcIndex - 1 + + if (prevIdx < 0) return '' + + return generatePath(RoutePaths.CredentialsId, { + claimId: getClaimIdFromVC(vcs[prevIdx]), + }) + }, [vcIndex, vcs]) + + return ( + + + + + Back + + + + + {vc && issuerDetails ? ( + + + + + + + + + + + + + + + + + + Generate proof + + + Get info + + + + Remove Credential + + + + + + ) : ( + + View all credentials + + } + /> + )} + + + ) +} diff --git a/src/pages/Credentials/pages/CredentialsList/index.tsx b/src/pages/Credentials/pages/CredentialsList/index.tsx index e1752a82..45d6233b 100644 --- a/src/pages/Credentials/pages/CredentialsList/index.tsx +++ b/src/pages/Credentials/pages/CredentialsList/index.tsx @@ -1,28 +1,66 @@ -import { Badge, Stack, StackProps } from '@mui/material' +import { Alert, Box, Grid, Paper, Skeleton, Stack, StackProps } from '@mui/material' +import isEmpty from 'lodash/isEmpty' import { useTranslation } from 'react-i18next' -import { NavLink } from 'react-router-dom' +import { generatePath, NavLink } from 'react-router-dom' -import { PageTitles } from '@/common' +import { getClaimIdFromVC } from '@/api/modules/zkp' +import { CredentialCard, NoDataViewer, PageTitles } from '@/common' import { RoutePaths } from '@/enums' -import { useCredentialsContext } from '@/pages/Credentials/contexts' -import { UiIcon } from '@/ui' +import { useLoading } from '@/hooks' +import { credentialsStore, useCredentialsState } from '@/store' +import { UiButton } from '@/ui' type Props = StackProps export default function CredentialsList({ ...rest }: Props) { const { t } = useTranslation() - const { orgGroupRequests } = useCredentialsContext() + const { vcs, issuersDetails } = useCredentialsState() + + const { isLoading, isLoadingError } = useLoading(undefined, () => credentialsStore.load(), { + loadOnMount: !vcs.length, + loadArgs: [vcs], + }) return ( - -
- - - - - + + + + {isLoading ? ( + + + + + + + + + ) : isLoadingError ? ( + {`There's an error occurred, please, reload page`} + ) : !vcs.length || isEmpty(issuersDetails) ? ( + Load Credentials} + /> + ) : ( + + {vcs.map((vc, idx) => ( + + + + + + ))} + + )} +
) } diff --git a/src/pages/Credentials/pages/CredentialsRequests/index.tsx b/src/pages/Credentials/pages/CredentialsRequests/index.tsx index 7ca93292..3f3df15f 100644 --- a/src/pages/Credentials/pages/CredentialsRequests/index.tsx +++ b/src/pages/Credentials/pages/CredentialsRequests/index.tsx @@ -7,7 +7,6 @@ import { OrgGroupRequestStatuses, OrgGroupRequestWithClaims } from '@/api/module import { PageTitles } from '@/common' import { BusEvents, RoutePaths } from '@/enums' import { bus } from '@/helpers' -import { useCredentialsContext } from '@/pages/Credentials/contexts' import { UiButton, UiIcon } from '@/ui' import { ClaimVCsModal, FillRequestFormDrawer } from './components' @@ -17,7 +16,8 @@ type Props = StackProps export default function CredentialsRequests({ ...rest }: Props) { const { t } = useTranslation() - const { orgGroupRequests } = useCredentialsContext() + // TODO: implement backend once ready + const [orgGroupRequests] = useState([]) const [isFillFormDrawerShown, setIsFillFormDrawerShown] = useState(false) const [isClaimModalShown, setIsClaimModalShown] = useState(false) diff --git a/src/pages/Credentials/pages/index.ts b/src/pages/Credentials/pages/index.ts index 1fa0783d..7983833b 100644 --- a/src/pages/Credentials/pages/index.ts +++ b/src/pages/Credentials/pages/index.ts @@ -1,2 +1,3 @@ +export { default as CredentialsId } from './CredentialsId' export { default as CredentialsList } from './CredentialsList' export { default as CredentialsRequests } from './CredentialsRequests' diff --git a/src/pages/UiKit/UiKitOther.tsx b/src/pages/UiKit/UiKitOther.tsx index 0b02a3bb..059197bb 100644 --- a/src/pages/UiKit/UiKitOther.tsx +++ b/src/pages/UiKit/UiKitOther.tsx @@ -4,6 +4,7 @@ import { useState } from 'react' import { BusEvents, Icons } from '@/enums' import { bus } from '@/helpers' import { UiBasicModal, UiButton, UiDrawer, UiIcon, UiModal } from '@/ui' +import UiPopup from '@/ui/UiPopup' export default function UiKitOther() { const [isDrawerShown, setIsDrawerShown] = useState(false) @@ -32,6 +33,31 @@ export default function UiKitOther() { + + {`Popup`} + + {`Popup`}} + menuItems={[ + } + >{`Item 1`}, + } + >{`Item 2`}, + } + >{`Item 3`}, + ]} + /> + + {`Drawer`} { }, { path: createDeepPath(RoutePaths.Credentials), + loader: authProtectedGuard, element: , }, { diff --git a/src/store/modules/credentials.module.ts b/src/store/modules/credentials.module.ts new file mode 100644 index 00000000..552ef308 --- /dev/null +++ b/src/store/modules/credentials.module.ts @@ -0,0 +1,43 @@ +import { W3CCredential } from '@rarimo/rarime-connector' + +import { zkpSnap } from '@/api/clients' +import { getIssuerDetails, IssuerDetails } from '@/api/modules/zkp' +import { createStore } from '@/helpers' + +type CredentialsState = { + vcs: W3CCredential[] + issuersDetails: Record +} + +const [credentialsStore, useCredentialsState] = createStore( + 'credentials', + { + vcs: [], + issuersDetails: {}, + } as CredentialsState, + state => ({ + load: async () => { + const vcs = await zkpSnap.getCredentials() + + const issuerDids = new Set(vcs.map(vc => vc.issuer)) + + const issuersDetails = ( + await Promise.all([...issuerDids].map(issuerDid => getIssuerDetails(issuerDid))) + ).reduce((acc, issuerDetails) => { + acc[issuerDetails.did] = issuerDetails + + return acc + }, {} as Record) + + state.vcs = vcs + state.issuersDetails = issuersDetails + + return { + vcs, + issuersDetails, + } + }, + }), +) + +export { credentialsStore, useCredentialsState } diff --git a/src/store/modules/identity.module.ts b/src/store/modules/identity.module.ts index 39c7c401..0361f758 100644 --- a/src/store/modules/identity.module.ts +++ b/src/store/modules/identity.module.ts @@ -28,6 +28,9 @@ const [identityStore, useIdentityState] = createStore( state.userDidBigIntString = identityIdBigIntString }, }), + { + isPersist: false, + }, ) export { identityStore, useIdentityState } diff --git a/src/store/modules/index.ts b/src/store/modules/index.ts index aa475cf3..40047c35 100644 --- a/src/store/modules/index.ts +++ b/src/store/modules/index.ts @@ -1,4 +1,5 @@ export * from './auth.module' +export * from './credentials.module' export * from './identity.module' export * from './ui.module' export * from './web3.module' diff --git a/src/store/modules/web3.module.ts b/src/store/modules/web3.module.ts index 515c659b..ab8ad2db 100644 --- a/src/store/modules/web3.module.ts +++ b/src/store/modules/web3.module.ts @@ -1,5 +1,6 @@ import { isMetamaskInstalled, isSnapInstalled } from '@rarimo/rarime-connector' +import { config } from '@/config' import { createStore } from '@/helpers' import { SUPPORTED_PROVIDERS } from '@/types' @@ -22,7 +23,7 @@ const [web3Store, useWeb3State] = createStore( }, checkSnapStatus: async () => { state.isMetamaskInstalled = await isMetamaskInstalled() - state.isSnapInstalled = await isSnapInstalled() + state.isSnapInstalled = await isSnapInstalled(...config.SNAP_V_PARAMS) return { isMetamaskInstalled: state.isMetamaskInstalled, diff --git a/src/theme/components.ts b/src/theme/components.ts index 5b447387..3f9754ce 100644 --- a/src/theme/components.ts +++ b/src/theme/components.ts @@ -126,10 +126,18 @@ export const components: Components> = { }, }, MuiPaper: { + defaultProps: { + variant: 'outlined', + square: false, + }, styleOverrides: { - root: { + root: ({ theme }) => ({ backgroundImage: 'unset', - }, + background: theme.palette.background.light, + padding: theme.spacing(6), + borderColor: theme.palette.additional.layerBorder, + borderRadius: theme.spacing(4), + }), }, }, MuiTextField: { diff --git a/src/ui/UiPopup.tsx b/src/ui/UiPopup.tsx new file mode 100644 index 00000000..28470d01 --- /dev/null +++ b/src/ui/UiPopup.tsx @@ -0,0 +1,50 @@ +import { Menu, MenuItem } from '@mui/material' +import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state' +import { cloneElement, ComponentProps, ReactElement, useMemo } from 'react' +import { v4 as uuidv4 } from 'uuid' + +type Props = { + trigger: ReactElement + menuItems: ReactElement[] +} & Omit, 'children'>, 'variant'> + +export default function UiPopup({ trigger, menuItems, ...rest }: Props) { + const popupId = useMemo(() => uuidv4(), []) + + return ( + + {popupState => ( + <> + {cloneElement(trigger, bindTrigger(popupState))} + + + {menuItems.map((menuItem, idx) => ( + + {menuItem} + + ))} + + + )} + + ) +} diff --git a/yarn.lock b/yarn.lock index b21841d0..22af903d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1382,16 +1382,16 @@ __metadata: languageName: node linkType: hard -"@mui/core-downloads-tracker@npm:^5.15.9": - version: 5.15.9 - resolution: "@mui/core-downloads-tracker@npm:5.15.9" - checksum: 8ecf0da83b35454705de45884762bc2edd41a9eb9600db6dbf7f324c3e25e953fec310a23f799b00cda85c49149f4bafd79c4b285480823a2e426e16d9ad03ae +"@mui/core-downloads-tracker@npm:^5.15.10": + version: 5.15.10 + resolution: "@mui/core-downloads-tracker@npm:5.15.10" + checksum: d05da33eb3532994aa0b71e8131c8c21b4c6256976bfa704b2268c2bd88ae2c6853b0832c2af904507820e56563d373020827310abd626e146e026200b76eacf languageName: node linkType: hard "@mui/icons-material@npm:^5.14.19": - version: 5.15.9 - resolution: "@mui/icons-material@npm:5.15.9" + version: 5.15.10 + resolution: "@mui/icons-material@npm:5.15.10" dependencies: "@babel/runtime": "npm:^7.23.9" peerDependencies: @@ -1401,17 +1401,17 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 2f3eac547a59ee091f17c1504bf4bdd53fe0804d8a314ede074f70cdac18f5b72005b041b51381e09cf1e3c1c80f4f378c39ebbfa55dc307295da85e9df3d6e7 + checksum: d49747d962ad9dec172d498173ee60fe29f3c818d15458fdc21b5d7a1cfa3a5acf8c6f3e6e35c665a3b0f4a088ac6806601c0d408403117f6711740448db4346 languageName: node linkType: hard -"@mui/material@npm:^5.14.20": - version: 5.15.9 - resolution: "@mui/material@npm:5.15.9" +"@mui/material@npm:^5.0.0, @mui/material@npm:^5.14.20": + version: 5.15.10 + resolution: "@mui/material@npm:5.15.10" dependencies: "@babel/runtime": "npm:^7.23.9" "@mui/base": "npm:5.0.0-beta.36" - "@mui/core-downloads-tracker": "npm:^5.15.9" + "@mui/core-downloads-tracker": "npm:^5.15.10" "@mui/system": "npm:^5.15.9" "@mui/types": "npm:^7.2.13" "@mui/utils": "npm:^5.15.9" @@ -1434,7 +1434,7 @@ __metadata: optional: true "@types/react": optional: true - checksum: 1a0cb0566d2d24ef1fcb966560b37dd4e57482c10bf61ac7dcfedadb4fbdae5222639df9c313ce53e585450166fbb5a9f2f846bfff6deb432a4262141d215a3c + checksum: 057147a7f68269641954dd456852a801c71fe10cc1dee7c4d669e8327a792ac2dee4083c4562b918a3696276a9035ba2a5b1b5261dc7f1a385711b938ae508fe languageName: node linkType: hard @@ -1842,13 +1842,13 @@ __metadata: linkType: hard "@rarimo/rarime-connector@npm:^2.1.0-rc.2": - version: 2.1.0-rc.2 - resolution: "@rarimo/rarime-connector@npm:2.1.0-rc.2" + version: 2.1.0-rc.3 + resolution: "@rarimo/rarime-connector@npm:2.1.0-rc.3" dependencies: "@ethersproject/providers": "npm:5.7.2" compare-versions: "npm:^6.1.0" ethers: "npm:5.7.2" - checksum: 235886185c861b5cfec3f1ef4644becb84a1dd7b9cbbf029de6e1bd789cd2ae65e0e6aaccce22c3449317eedaf361368efa4cd0dd82a29f784f64f8215b81c3b + checksum: f0d4dd01ad6a34b4e73d37c67e1044e8a22e5da7c5f7e12b2e8c0e594dcd6b00b7e0553e775d23ac403b22090dad7b66e4f55c55eca341350d5a74d5f349f2ab languageName: node linkType: hard @@ -2103,91 +2103,91 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-darwin-arm64@npm:1.4.0" +"@swc/core-darwin-arm64@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-darwin-arm64@npm:1.4.1" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-darwin-x64@npm:1.4.0" +"@swc/core-darwin-x64@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-darwin-x64@npm:1.4.1" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.4.0" +"@swc/core-linux-arm-gnueabihf@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.4.1" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-linux-arm64-gnu@npm:1.4.0" +"@swc/core-linux-arm64-gnu@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-linux-arm64-gnu@npm:1.4.1" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-linux-arm64-musl@npm:1.4.0" +"@swc/core-linux-arm64-musl@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-linux-arm64-musl@npm:1.4.1" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-linux-x64-gnu@npm:1.4.0" +"@swc/core-linux-x64-gnu@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-linux-x64-gnu@npm:1.4.1" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-linux-x64-musl@npm:1.4.0" +"@swc/core-linux-x64-musl@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-linux-x64-musl@npm:1.4.1" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-win32-arm64-msvc@npm:1.4.0" +"@swc/core-win32-arm64-msvc@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-win32-arm64-msvc@npm:1.4.1" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-win32-ia32-msvc@npm:1.4.0" +"@swc/core-win32-ia32-msvc@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-win32-ia32-msvc@npm:1.4.1" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.4.0": - version: 1.4.0 - resolution: "@swc/core-win32-x64-msvc@npm:1.4.0" +"@swc/core-win32-x64-msvc@npm:1.4.1": + version: 1.4.1 + resolution: "@swc/core-win32-x64-msvc@npm:1.4.1" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.107": - version: 1.4.0 - resolution: "@swc/core@npm:1.4.0" - dependencies: - "@swc/core-darwin-arm64": "npm:1.4.0" - "@swc/core-darwin-x64": "npm:1.4.0" - "@swc/core-linux-arm-gnueabihf": "npm:1.4.0" - "@swc/core-linux-arm64-gnu": "npm:1.4.0" - "@swc/core-linux-arm64-musl": "npm:1.4.0" - "@swc/core-linux-x64-gnu": "npm:1.4.0" - "@swc/core-linux-x64-musl": "npm:1.4.0" - "@swc/core-win32-arm64-msvc": "npm:1.4.0" - "@swc/core-win32-ia32-msvc": "npm:1.4.0" - "@swc/core-win32-x64-msvc": "npm:1.4.0" - "@swc/counter": "npm:^0.1.1" + version: 1.4.1 + resolution: "@swc/core@npm:1.4.1" + dependencies: + "@swc/core-darwin-arm64": "npm:1.4.1" + "@swc/core-darwin-x64": "npm:1.4.1" + "@swc/core-linux-arm-gnueabihf": "npm:1.4.1" + "@swc/core-linux-arm64-gnu": "npm:1.4.1" + "@swc/core-linux-arm64-musl": "npm:1.4.1" + "@swc/core-linux-x64-gnu": "npm:1.4.1" + "@swc/core-linux-x64-musl": "npm:1.4.1" + "@swc/core-win32-arm64-msvc": "npm:1.4.1" + "@swc/core-win32-ia32-msvc": "npm:1.4.1" + "@swc/core-win32-x64-msvc": "npm:1.4.1" + "@swc/counter": "npm:^0.1.2" "@swc/types": "npm:^0.1.5" peerDependencies: "@swc/helpers": ^0.5.0 @@ -2215,11 +2215,11 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: aecf7f2ef421e6b9125c8f7d282420e637d22c213a42860af6dfd9fc932b22cd710cdec95e41d0cad4ba67ec7bf420e6e7a0d775856be2c58af6a52ffeaeb4b2 + checksum: ff5ac47c6a6b925292b82a98aefbf518dae6bb69300ff8aa231e10f0b4f68b346cbfce9a8dbe177119d43bdbf5bc7b0aaee8cc49d47385197bef8317df4c015c languageName: node linkType: hard -"@swc/counter@npm:^0.1.1": +"@swc/counter@npm:^0.1.2": version: 0.1.3 resolution: "@swc/counter@npm:0.1.3" checksum: 8424f60f6bf8694cfd2a9bca45845bce29f26105cda8cf19cdb9fd3e78dc6338699e4db77a89ae449260bafa1cc6bec307e81e7fb96dbf7dcfce0eea55151356 @@ -3623,14 +3623,15 @@ __metadata: linkType: hard "call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6": - version: 1.0.6 - resolution: "call-bind@npm:1.0.6" + version: 1.0.7 + resolution: "call-bind@npm:1.0.7" dependencies: + es-define-property: "npm:^1.0.0" es-errors: "npm:^1.3.0" function-bind: "npm:^1.1.2" - get-intrinsic: "npm:^1.2.3" - set-function-length: "npm:^1.2.0" - checksum: 8dd9d4dad0a3ba4d1799a7baccc1b8dd4948ec99c6c7e88a8ac80bd814a34c87cc2b14680f0e343d9904e9e4a0a5b8c08758022ca0d1ef13f2723cd3720159f0 + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.1" + checksum: a3ded2e423b8e2a265983dba81c27e125b48eefb2655e7dfab6be597088da3d47c47976c24bc51b8fd9af1061f8f87b4ab78a314f3c77784b2ae2ba535ad8b8d languageName: node linkType: hard @@ -3772,6 +3773,13 @@ __metadata: languageName: node linkType: hard +"classnames@npm:^2.2.6": + version: 2.5.1 + resolution: "classnames@npm:2.5.1" + checksum: afff4f77e62cea2d79c39962980bf316bacb0d7c49e13a21adaadb9221e1c6b9d3cdb829d8bb1b23c406f4e740507f37e1dcf506f7e3b7113d17c5bab787aa69 + languageName: node + linkType: hard + "clean-css@npm:^5.2.2": version: 5.3.3 resolution: "clean-css@npm:5.3.3" @@ -4250,14 +4258,14 @@ __metadata: linkType: hard "define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.2": - version: 1.1.2 - resolution: "define-data-property@npm:1.1.2" + version: 1.1.3 + resolution: "define-data-property@npm:1.1.3" dependencies: es-errors: "npm:^1.3.0" - get-intrinsic: "npm:^1.2.2" + get-intrinsic: "npm:^1.2.4" gopd: "npm:^1.0.1" has-property-descriptors: "npm:^1.0.1" - checksum: c496512a9b37c0a1708931a7ef419c120d23e672758fd41903f0593d6c48b2366976a484ad97bd45ed636da712dfa886c0908bf2c77b3f7ca36d7299b23a24bb + checksum: 918836b0ddcbefe9f351d35529bcbba5b00e4d867d1698b676eacfe50c0e479fb830dd4dd17988d3a0d2328707ef47a24d570cfcae9085b3e331f6236034256a languageName: node linkType: hard @@ -4566,9 +4574,9 @@ __metadata: linkType: hard "dotenv@npm:^16.0.0, dotenv@npm:^16.3.0, dotenv@npm:^16.3.1": - version: 16.4.2 - resolution: "dotenv@npm:16.4.2" - checksum: 04373a742f565896de1ab3eadcd15c1543b6d1fc37bb7ab2faa2286a0b3fd432aa02c6fd330ede758432f36865f0fb128afa2e5991e0a789b9011e720ba4a876 + version: 16.4.3 + resolution: "dotenv@npm:16.4.3" + checksum: c6a572b2dab5d71accb7064c90b38dfd4068c2487be859a0f053460fcaa685a7718e78db51d643b32e0736b318988c31f8c45cb4ab99cd620278f537177cb0ab languageName: node linkType: hard @@ -4784,6 +4792,15 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.0": + version: 1.0.0 + resolution: "es-define-property@npm:1.0.0" + dependencies: + get-intrinsic: "npm:^1.2.4" + checksum: 6bf3191feb7ea2ebda48b577f69bdfac7a2b3c9bcf97307f55fd6ef1bbca0b49f0c219a935aca506c993d8c5d8bddd937766cb760cd5e5a1071351f2df9f9aa4 + languageName: node + linkType: hard + "es-errors@npm:^1.0.0, es-errors@npm:^1.1.0, es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": version: 1.3.0 resolution: "es-errors@npm:1.3.0" @@ -6051,11 +6068,11 @@ __metadata: linkType: hard "has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1": - version: 1.0.1 - resolution: "has-property-descriptors@npm:1.0.1" + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" dependencies: - get-intrinsic: "npm:^1.2.2" - checksum: d62ba94b40150b00d621bc64a6aedb5bf0ee495308b4b7ed6bac856043db3cdfb1db553ae81cec91c9d2bd82057ff0e94145e7fa25d5aa5985ed32e0921927f6 + es-define-property: "npm:^1.0.0" + checksum: 253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 languageName: node linkType: hard @@ -6248,12 +6265,12 @@ __metadata: linkType: hard "http-proxy-agent@npm:^7.0.0": - version: 7.0.0 - resolution: "http-proxy-agent@npm:7.0.0" + version: 7.0.1 + resolution: "http-proxy-agent@npm:7.0.1" dependencies: agent-base: "npm:^7.1.0" debug: "npm:^4.3.4" - checksum: a11574ff39436cee3c7bc67f259444097b09474605846ddd8edf0bf4ad8644be8533db1aa463426e376865047d05dc22755e638632819317c0c2f1b2196657c8 + checksum: 09bc7ea134a133caeb57c9487e6ee59c61aebdc8f531a86c4638cffc0d04cf7415b9d74d32e6e7781e1e931e882d1163c953430aefd6fbd1465b5e033432a040 languageName: node linkType: hard @@ -6272,12 +6289,12 @@ __metadata: linkType: hard "https-proxy-agent@npm:^7.0.1": - version: 7.0.2 - resolution: "https-proxy-agent@npm:7.0.2" + version: 7.0.3 + resolution: "https-proxy-agent@npm:7.0.3" dependencies: agent-base: "npm:^7.0.2" debug: "npm:4" - checksum: 7735eb90073db087e7e79312e3d97c8c04baf7ea7ca7b013382b6a45abbaa61b281041a98f4e13c8c80d88f843785bcc84ba189165b4b4087b1e3496ba656d77 + checksum: 0a5d4beb8f3e71755e3dc62e421aceb3df8bf0d20892dfbdc54de7791f9d34be222c6acb8c83930b138a4bab9387a9f4e06ea0b171cda8e167c3aa72d437f8d8 languageName: node linkType: hard @@ -7486,6 +7503,20 @@ __metadata: languageName: node linkType: hard +"material-ui-popup-state@npm:^5.0.10": + version: 5.0.10 + resolution: "material-ui-popup-state@npm:5.0.10" + dependencies: + "@babel/runtime": "npm:^7.20.6" + "@mui/material": "npm:^5.0.0" + classnames: "npm:^2.2.6" + prop-types: "npm:^15.7.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: c4a42f37f0f230d5f0830152c233305fac228137249c20d6144e73729eb00939a2f02423708d37eb0cb440e873a2974592f049388fe3fd0cfd0ae64f209c3f55 + languageName: node + linkType: hard + "md5.js@npm:^1.3.4": version: 1.3.5 resolution: "md5.js@npm:1.3.5" @@ -8745,7 +8776,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.6.2, prop-types@npm:^15.8.1": +"prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -8949,6 +8980,7 @@ __metadata: jdenticon: "npm:^3.2.0" lodash: "npm:^4.17.21" loglevel: "npm:^1.8.1" + material-ui-popup-state: "npm:^5.0.10" notistack: "npm:^3.0.1" postcss: "npm:^8.4.24" prettier: "npm:^2.7.1" @@ -9564,7 +9596,7 @@ __metadata: languageName: node linkType: hard -"set-function-length@npm:^1.2.0": +"set-function-length@npm:^1.2.1": version: 1.2.1 resolution: "set-function-length@npm:1.2.1" dependencies: