From 1656fc0e25dcd378f74bc55d9aa6486a4ece667b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 25 Jun 2024 16:30:38 +0200 Subject: [PATCH 1/6] temp Signed-off-by: Timo Glastra --- .vscode/settings.json | 3 +- apps/funke/app/_layout.tsx | 105 ++++----- apps/funke/babel.config.js | 2 + apps/funke/package.json | 1 + packages/agent/src/display.ts | 5 + .../agent/src/format/formatPresentation.ts | 68 +++--- .../hooks/useDidCommPresentationActions.ts | 170 ++++++++------ packages/agent/src/invitation/handler.ts | 21 +- .../DidCommPresentationNotificationScreen.tsx | 15 +- .../OpenIdPresentationNotificationScreen.tsx | 12 + .../PresentationNotificationScreen.tsx | 215 ++++++++++++------ packages/ui/src/content/Icon.tsx | 1 + packages/ui/src/panels/Sheet.tsx | 65 ++---- pnpm-lock.yaml | 77 ++++++- 14 files changed, 491 insertions(+), 269 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4d360cbc..e78ca2e4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "editor.defaultFormatter": "biomejs.biome" } diff --git a/apps/funke/app/_layout.tsx b/apps/funke/app/_layout.tsx index 835f9432..01b82ef0 100644 --- a/apps/funke/app/_layout.tsx +++ b/apps/funke/app/_layout.tsx @@ -9,6 +9,7 @@ import { useFonts } from 'expo-font' import { Stack } from 'expo-router' import * as SplashScreen from 'expo-splash-screen' import { useEffect, useState } from 'react' +import { GestureHandlerRootView } from 'react-native-gesture-handler' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { DeeplinkHandler } from './utils' @@ -107,57 +108,59 @@ export default function HomeLayout() { return ( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + ) } diff --git a/apps/funke/babel.config.js b/apps/funke/babel.config.js index e053a657..cab5f28f 100644 --- a/apps/funke/babel.config.js +++ b/apps/funke/babel.config.js @@ -17,6 +17,8 @@ module.exports = (api) => { disableExtraction: process.env.NODE_ENV === 'development', }, ], + // used for bottom sheet + 'react-native-reanimated/plugin', ], } } diff --git a/apps/funke/package.json b/apps/funke/package.json index 70ae84b3..707abc3b 100644 --- a/apps/funke/package.json +++ b/apps/funke/package.json @@ -10,6 +10,7 @@ "prebuild": "APP_VARIANT=development expo prebuild --no-install" }, "dependencies": { + "@gorhom/bottom-sheet": "^4.6.3", "@hyperledger/anoncreds-react-native": "^0.2.2", "@hyperledger/aries-askar-react-native": "^0.2.0", "@hyperledger/indy-vdr-react-native": "^0.2.0", diff --git a/packages/agent/src/display.ts b/packages/agent/src/display.ts index c572def3..fa8ac113 100644 --- a/packages/agent/src/display.ts +++ b/packages/agent/src/display.ts @@ -202,6 +202,11 @@ function getW3cCredentialDisplay( } } + // Use background color from the JFF credential if not provided by the OID4VCI metadata + if (!credentialDisplay.backgroundColor && jffCredential.credentialBranding?.backgroundColor) { + credentialDisplay.backgroundColor = jffCredential.credentialBranding.backgroundColor + } + return { ...credentialDisplay, // Last fallback, if there's really no name for the credential, we use a generic name diff --git a/packages/agent/src/format/formatPresentation.ts b/packages/agent/src/format/formatPresentation.ts index 781c09e3..6855d7f2 100644 --- a/packages/agent/src/format/formatPresentation.ts +++ b/packages/agent/src/format/formatPresentation.ts @@ -12,51 +12,51 @@ export interface FormattedSubmission { } export interface FormattedSubmissionEntry { - name: string + /** can be either AnonCreds groupName or PEX inputDescriptorId */ + inputDescriptorId: string isSatisfied: boolean - credentialName: string - issuerName?: string + + name: string description?: string - requestedAttributes?: string[] - backgroundColor?: string + + credentials: Array<{ + credentialName: string + issuerName?: string + requestedAttributes?: string[] + backgroundColor?: string + }> } export function formatDifPexCredentialsForRequest( credentialsForRequest: DifPexCredentialsForRequest ): FormattedSubmission { const entries = credentialsForRequest.requirements.flatMap((requirement) => { - return requirement.submissionEntry.map((submission) => { - // FIXME: support credential selection from JFF branch - const [firstVerifiableCredential] = submission.verifiableCredentials - if (firstVerifiableCredential) { - // Credential can be satisfied - const { display, credential } = getCredentialForDisplay(firstVerifiableCredential.credentialRecord) - - // TODO: support nesting - let requestedAttributes: string[] - if (firstVerifiableCredential.type === ClaimFormat.SdJwtVc) { - const { metadata, visibleProperties } = filterAndMapSdJwtKeys(firstVerifiableCredential.disclosedPayload) - requestedAttributes = [...Object.keys(visibleProperties), ...Object.keys(metadata)] - } else { - requestedAttributes = Object.keys(credential?.credentialSubject ?? {}) - } - - return { - name: submission.name ?? 'Unknown', - description: submission.purpose, - isSatisfied: true, - credentialName: display.name, - issuerName: display.issuer.name, - requestedAttributes, - backgroundColor: display.backgroundColor, - } - } + return requirement.submissionEntry.map((submission): FormattedSubmissionEntry => { return { + inputDescriptorId: submission.inputDescriptorId, name: submission.name ?? 'Unknown', description: submission.purpose, - isSatisfied: false, - // fallback to submission name because there is no credential - credentialName: submission.name ?? 'Credential name', + isSatisfied: submission.verifiableCredentials.length >= 1, + + credentials: submission.verifiableCredentials.map((verifiableCredential) => { + const { display, credential } = getCredentialForDisplay(verifiableCredential.credentialRecord) + + // TODO: support nesting + let requestedAttributes: string[] + if (verifiableCredential.type === ClaimFormat.SdJwtVc) { + const { metadata, visibleProperties } = filterAndMapSdJwtKeys(verifiableCredential.disclosedPayload) + requestedAttributes = [...Object.keys(visibleProperties), ...Object.keys(metadata)] + } else { + requestedAttributes = Object.keys(credential?.credentialSubject ?? {}) + } + + return { + credentialName: display.name, + issuerName: display.issuer.name, + requestedAttributes, + backgroundColor: display.backgroundColor, + } + }), } }) }) diff --git a/packages/agent/src/hooks/useDidCommPresentationActions.ts b/packages/agent/src/hooks/useDidCommPresentationActions.ts index f712d33e..b4ee7baa 100644 --- a/packages/agent/src/hooks/useDidCommPresentationActions.ts +++ b/packages/agent/src/hooks/useDidCommPresentationActions.ts @@ -1,9 +1,11 @@ import type { FormattedSubmission } from '../format/formatPresentation' import type { + AnonCredsCredentialsForProofRequest, AnonCredsPredicateType, AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicate, AnonCredsRequestedPredicateMatch, + AnonCredsSelectedCredentials, } from '@credo-ts/anoncreds' import type { ProofStateChangedEvent } from '@credo-ts/core' @@ -22,20 +24,25 @@ export function useDidCommPresentationActions(proofExchangeId: string) { const proofExchange = useProofById(proofExchangeId) const connection = useConnectionById(proofExchange?.connectionId ?? '') + let formatKey: 'anoncreds' | 'indy' | undefined = undefined + let anonCredsCredentials: AnonCredsCredentialsForProofRequest | undefined + const { data } = useQuery({ queryKey: ['didCommPresentationSubmission', proofExchangeId], queryFn: async (): Promise => { + formatKey = undefined + anonCredsCredentials = undefined const repository = agent.dependencyManager.resolve(CredentialRepository) const formatData = await agent.proofs.getFormatData(proofExchangeId) + const proofRequest = formatData.request?.anoncreds ?? formatData.request?.indy const credentialsForRequest = await agent.proofs.getCredentialsForRequest({ proofRecordId: proofExchangeId, }) - const anonCredsCredentials = - credentialsForRequest.proofFormats.anoncreds ?? credentialsForRequest.proofFormats.indy - + formatKey = formatData.request?.anoncreds !== undefined ? 'anoncreds' : 'indy' + anonCredsCredentials = credentialsForRequest.proofFormats.anoncreds ?? credentialsForRequest.proofFormats.indy if (!anonCredsCredentials || !proofRequest) { throw new CredoError('Invalid proof request.') } @@ -46,78 +53,81 @@ export function useDidCommPresentationActions(proofExchangeId: string) { name: proofRequest?.name ?? 'Unknown', } + const allCredentialIds = [ + ...Object.values(anonCredsCredentials.attributes).flatMap((matches) => + matches.map((match) => match.credentialId) + ), + ...Object.values(anonCredsCredentials.predicates).flatMap((matches) => + matches.map((match) => match.credentialId) + ), + ] + const credentialExchanges = await repository.findByQuery(agent.context, { + $or: allCredentialIds.map((credentialId) => ({ credentialIds: [credentialId] })), + }) + + const attributes = anonCredsCredentials.attributes await Promise.all( - Object.keys(anonCredsCredentials.attributes).map(async (groupName) => { + Object.keys(attributes).map(async (groupName) => { const requestedAttribute = proofRequest.requested_attributes[groupName] const attributeNames = requestedAttribute?.names ?? [requestedAttribute?.name as string] - const attributeArray = anonCredsCredentials.attributes[groupName] as AnonCredsRequestedAttributeMatch[] - - const firstMatch = attributeArray[0] - - if (!firstMatch) { - submission.entries.push({ - credentialName: 'Credential', // TODO: we can extract this from the schema name, but we would have to fetch it - isSatisfied: false, - name: groupName, // TODO - requestedAttributes: attributeNames, - }) - } else { - const credentialExchange = await repository.findSingleByQuery(agent.context, { - credentialIds: [firstMatch.credentialId], - }) - - const credentialDisplayMetadata = credentialExchange - ? getDidCommCredentialExchangeDisplayMetadata(credentialExchange) - : undefined - - submission.entries.push({ - name: groupName, // TODO: humanize string? Or should we let this out? - credentialName: credentialDisplayMetadata?.credentialName ?? 'Credential', - isSatisfied: true, - issuerName: credentialDisplayMetadata?.issuerName ?? 'Unknown', - requestedAttributes: attributeNames, - }) - } + const attributeArray = attributes[groupName] as AnonCredsRequestedAttributeMatch[] + + submission.entries.push({ + inputDescriptorId: groupName, + isSatisfied: attributeArray.length >= 1, + name: groupName, // TODO + credentials: attributeArray.map((attribute) => { + const credentialExchange = credentialExchanges.find((c) => + c.credentials.find((cc) => cc.credentialRecordId === attribute.credentialId) + ) + const credentialDisplayMetadata = credentialExchange + ? getDidCommCredentialExchangeDisplayMetadata(credentialExchange) + : undefined + + return { + name: groupName, // TODO: humanize string? Or should we let this out? + credentialName: credentialDisplayMetadata?.credentialName ?? 'Credential', + isSatisfied: true, + issuerName: credentialDisplayMetadata?.issuerName ?? 'Unknown', + requestedAttributes: attributeNames, + } + }), + }) }) ) + const predicates = anonCredsCredentials.predicates await Promise.all( - Object.keys(anonCredsCredentials.predicates).map(async (groupName) => { + Object.keys(predicates).map(async (groupName) => { const requestedPredicate = proofRequest.requested_predicates[groupName] - const predicateArray = anonCredsCredentials.predicates[groupName] as AnonCredsRequestedPredicateMatch[] + const predicateArray = predicates[groupName] as AnonCredsRequestedPredicateMatch[] if (!requestedPredicate) { throw new Error('Invalid presentation request') } - // FIXME: we need to still filter based on the predicate (e.g. age is actually >= 18) - // This should probably be fixed in AFJ. - const firstMatch = predicateArray[0] - - if (!firstMatch) { - submission.entries.push({ - credentialName: 'Credential', // TODO: we can extract this from the schema name, but we would have to fetch it - isSatisfied: false, - name: groupName, // TODO - requestedAttributes: [formatPredicate(requestedPredicate)], - }) - } else { - const credentialExchange = await repository.findSingleByQuery(agent.context, { - credentialIds: [firstMatch.credentialId], - }) - - const credentialDisplayMetadata = credentialExchange - ? getDidCommCredentialExchangeDisplayMetadata(credentialExchange) - : undefined - - submission.entries.push({ - name: groupName, // TODO: humanize string? Or should we let this out? - credentialName: credentialDisplayMetadata?.credentialName ?? 'Credential', - isSatisfied: true, - issuerName: credentialDisplayMetadata?.issuerName ?? 'Unknown', - requestedAttributes: [formatPredicate(requestedPredicate)], - }) - } + submission.entries.push({ + inputDescriptorId: groupName, + isSatisfied: predicateArray.length >= 1, + name: groupName, // TODO + credentials: predicateArray.map((predicate) => { + const credentialExchange = credentialExchanges.find((c) => + c.credentials.find((cc) => cc.credentialRecordId === predicate.credentialId) + ) + const credentialDisplayMetadata = credentialExchange + ? getDidCommCredentialExchangeDisplayMetadata(credentialExchange) + : undefined + + return { + name: groupName, // TODO: humanize string? Or should we let this out? + credentialName: credentialDisplayMetadata?.credentialName ?? 'Credential', + isSatisfied: true, + issuerName: credentialDisplayMetadata?.issuerName ?? 'Unknown', + // TODO: we need to group multiple predicates/attributes for the same credential into one. + requestedAttributes: [formatPredicate(requestedPredicate)], + } + }), + }) }) ) @@ -129,7 +139,35 @@ export function useDidCommPresentationActions(proofExchangeId: string) { const { mutateAsync: acceptMutateAsync, status: acceptStatus } = useMutation({ mutationKey: ['acceptDidCommPresentation', proofExchangeId], - mutationFn: async () => { + mutationFn: async (selectedCredentials?: { [groupName: string]: number }) => { + let formatInput: { indy?: AnonCredsSelectedCredentials; anoncreds?: AnonCredsSelectedCredentials } | undefined = + undefined + + if (selectedCredentials && Object.keys(selectedCredentials).length > 0) { + if (!formatKey || !anonCredsCredentials) throw new Error('Unable to accept presentation without credentials') + const selectedAttributes = Object.fromEntries( + Object.entries(anonCredsCredentials.attributes).map(([groupName, matches]) => [ + groupName, + matches[selectedCredentials[groupName] ?? 0], + ]) + ) + + const selectedPredicates = Object.fromEntries( + Object.entries(anonCredsCredentials.predicates).map(([groupName, matches]) => [ + groupName, + matches[selectedCredentials[groupName] ?? 0], + ]) + ) + + formatInput = { + [formatKey]: { + attributes: selectedAttributes, + predicates: selectedPredicates, + selfAttestedAttributes: {}, + }, + } + } + const presentationDone$ = agent.events.observable(ProofEventTypes.ProofStateChanged).pipe( // Correct record with id and state filter( @@ -143,8 +181,10 @@ export function useDidCommPresentationActions(proofExchangeId: string) { ) const presentationDonePromise = firstValueFrom(presentationDone$) - - await agent.proofs.acceptRequest({ proofRecordId: proofExchangeId }) + await agent.proofs.acceptRequest({ + proofRecordId: proofExchangeId, + proofFormats: formatInput, + }) await presentationDonePromise }, }) diff --git a/packages/agent/src/invitation/handler.ts b/packages/agent/src/invitation/handler.ts index 8402a5ce..59c70624 100644 --- a/packages/agent/src/invitation/handler.ts +++ b/packages/agent/src/invitation/handler.ts @@ -226,15 +226,30 @@ export const shareProof = async ({ agent, authorizationRequest, credentialsForRequest, + selectedCredentials, }: { agent: FullAppAgent authorizationRequest: OpenId4VcSiopVerifiedAuthorizationRequest - // TODO: support selection credentialsForRequest: DifPexCredentialsForRequest + selectedCredentials: { [inputDescriptorId: string]: number } }) => { - const presentationExchangeService = agent.dependencyManager.resolve(DifPresentationExchangeService) + if (!credentialsForRequest.areRequirementsSatisfied) { + throw new Error('Requirements from proof request are not satisfied') + } + + // Map all requirements and entries to a credential record. If a credential record for an + // input descriptor has been provided in `selectedCredentials` we will use that. Otherwise + // it will pick the first available credential. + const credentials = Object.fromEntries( + credentialsForRequest.requirements.flatMap((requirement) => + requirement.submissionEntry.map((entry) => { + const vcIndex = selectedCredentials[entry.inputDescriptorId] ?? 0 + + return [entry.inputDescriptorId, [entry.verifiableCredentials[vcIndex].credentialRecord]] + }) + ) + ) - const credentials = presentationExchangeService.selectCredentialsForRequest(credentialsForRequest) const result = await agent.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ authorizationRequest, presentationExchange: { diff --git a/packages/app/src/features/notifications/DidCommPresentationNotificationScreen.tsx b/packages/app/src/features/notifications/DidCommPresentationNotificationScreen.tsx index 223d4be5..07044dce 100644 --- a/packages/app/src/features/notifications/DidCommPresentationNotificationScreen.tsx +++ b/packages/app/src/features/notifications/DidCommPresentationNotificationScreen.tsx @@ -1,6 +1,6 @@ import { useDidCommPresentationActions, useAgent } from '@package/agent' import { useToastController } from '@package/ui' -import React from 'react' +import React, { useState } from 'react' import { useRouter } from 'solito/router' import { GettingInformationScreen } from './components/GettingInformationScreen' @@ -19,6 +19,10 @@ export function DidCommPresentationNotificationScreen({ proofExchangeId }: DidCo const { acceptPresentation, declinePresentation, proofExchange, acceptStatus, submission, verifierName } = useDidCommPresentationActions(proofExchangeId) + const [selectedCredentials, setSelectedCredentials] = useState<{ + [groupName: string]: number + }>({}) + const pushToWallet = () => { router.back() router.push('/') @@ -29,7 +33,7 @@ export function DidCommPresentationNotificationScreen({ proofExchangeId }: DidCo } const onProofAccept = () => { - acceptPresentation() + acceptPresentation(selectedCredentials) .then(() => { toast.show('Information has been successfully shared.') }) @@ -58,6 +62,13 @@ export function DidCommPresentationNotificationScreen({ proofExchangeId }: DidCo // If state is not idle, it means we have pressed accept isAccepting={acceptStatus !== 'idle'} verifierName={verifierName} + selectedCredentials={selectedCredentials} + onSelectCredentialForInputDescriptor={(groupName: string, vcIndex: number) => + setSelectedCredentials((selectedCredentials) => ({ + ...selectedCredentials, + [groupName]: vcIndex, + })) + } /> ) } diff --git a/packages/app/src/features/notifications/OpenIdPresentationNotificationScreen.tsx b/packages/app/src/features/notifications/OpenIdPresentationNotificationScreen.tsx index 463089c4..1729292b 100644 --- a/packages/app/src/features/notifications/OpenIdPresentationNotificationScreen.tsx +++ b/packages/app/src/features/notifications/OpenIdPresentationNotificationScreen.tsx @@ -30,6 +30,10 @@ export function OpenIdPresentationNotificationScreen() { [credentialsForRequest] ) + const [selectedCredentials, setSelectedCredentials] = useState<{ + [inputDescriptorId: string]: number + }>({}) + const pushToWallet = useCallback(() => { router.back() router.push('/') @@ -68,6 +72,7 @@ export function OpenIdPresentationNotificationScreen() { agent, authorizationRequest: credentialsForRequest.authorizationRequest, credentialsForRequest: credentialsForRequest.credentialsForRequest, + selectedCredentials, }) .then(() => { toast.show('Information has been successfully shared.') @@ -95,6 +100,13 @@ export function OpenIdPresentationNotificationScreen() { submission={submission} isAccepting={isSharing} verifierName={credentialsForRequest.verifierHostName} + selectedCredentials={selectedCredentials} + onSelectCredentialForInputDescriptor={(inputDescriptorId: string, index: number) => + setSelectedCredentials((selectedCredentials) => ({ + ...selectedCredentials, + [inputDescriptorId]: index, + })) + } /> ) } diff --git a/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx b/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx index 1a6836d0..08c9268d 100644 --- a/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx +++ b/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx @@ -1,11 +1,24 @@ +import type BottomSheet from '@gorhom/bottom-sheet' import type { FormattedSubmission } from '@package/agent' -import { YStack, Heading, Button, ScrollView, Paragraph } from '@package/ui' +import { + YStack, + Heading, + Button, + ScrollView, + Paragraph, + BottomSheetScrollView, + Sheet, + Stack, + XStack, + RefreshCw, +} from '@package/ui' import { sanitizeString } from '@package/utils' -import React from 'react' +import React, { useEffect, useRef, useState } from 'react' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { DualResponseButtons, CredentialRowCard } from '../../../components' +import { useNavigation } from 'expo-router' interface PresentationNotificationScreenProps { submission: FormattedSubmission @@ -13,6 +26,8 @@ interface PresentationNotificationScreenProps { onAccept: () => void onDecline: () => void verifierName?: string + selectedCredentials: { [inputDescriptorId: string]: number } + onSelectCredentialForInputDescriptor: (inputDescriptorId: string, index: number) => void } export function PresentationNotificationScreen({ @@ -21,79 +36,143 @@ export function PresentationNotificationScreen({ isAccepting, submission, verifierName, + selectedCredentials, + onSelectCredentialForInputDescriptor, }: PresentationNotificationScreenProps) { + const [changeSubmissionCredentialIndex, setChangeSubmissionCredentialIndex] = useState(-1) const { bottom } = useSafeAreaInsets() + + const currentSubmissionEntry = + changeSubmissionCredentialIndex !== -1 ? submission.entries[changeSubmissionCredentialIndex] : undefined + + const navigation = useNavigation() + const ref = useRef(null) + + useEffect(() => { + if (currentSubmissionEntry) { + ref.current?.expand() + } else { + ref.current?.close() + } + }, [currentSubmissionEntry]) + + useEffect(() => { + navigation.setOptions({ + gestureEnabled: true, + }) + }, [navigation]) + return ( - - - - - - You have received an information request - {verifierName ? ` from ${verifierName}` : ''}. - - {submission.purpose && ( - - {submission.purpose} - - )} - - - {submission.entries.map((s) => ( - - - - - {s.description && ( - - {s.description} - - )} - - {s.isSatisfied && s.requestedAttributes ? ( - - The following information will be presented: - - {s.requestedAttributes.map((a) => ( - - • {sanitizeString(a)} + <> + + + + + + You have received an information request + {verifierName ? ` from ${verifierName}` : ''}. + + {submission.purpose && ( + + {submission.purpose} + + )} + + + {submission.entries.map((s, i) => { + const selectedCredentialIndex = selectedCredentials[s.inputDescriptorId] ?? 0 + const selectedCredential = s.credentials[selectedCredentialIndex] + + return ( + + 1 ? () => setChangeSubmissionCredentialIndex(i) : undefined} + pressStyle={{ backgroundColor: s.isSatisfied ? '$grey-100' : undefined }} + > + + + + + + {s.credentials.length > 1 && } + + {s.description && ( + + {s.description} - ))} + )} + {s.isSatisfied && selectedCredential?.requestedAttributes ? ( + + The following information will be presented: + + {selectedCredential.requestedAttributes.map((a) => ( + + • {sanitizeString(a)} + + ))} + + + ) : ( + + This credential is not present in your wallet. + + )} - ) : ( - - This credential is not present in your wallet. - - )} - - - ))} + + ) + })} + + {submission.areAllSatisfied ? ( + + ) : ( + + + You don't have the required credentials to satisfy this request. + + Close + + )} - {submission.areAllSatisfied ? ( - - ) : ( - - - You don't have the required credentials to satisfy this request. - - Close - - )} - - + + + + + {currentSubmissionEntry?.credentials.map((c, credentialIndex) => ( + { + onSelectCredentialForInputDescriptor(currentSubmissionEntry.inputDescriptorId, credentialIndex) + setChangeSubmissionCredentialIndex(-1) + }} + // The index is stable enough here + // biome-ignore lint/suspicious/noArrayIndexKey: + key={credentialIndex} + issuer={c.issuerName} + name={c.credentialName} + hideBorder={credentialIndex === currentSubmissionEntry.credentials.length - 1} + bgColor={c.backgroundColor} + /> + ))} + + + + ) } diff --git a/packages/ui/src/content/Icon.tsx b/packages/ui/src/content/Icon.tsx index 4eabfbf1..77253559 100644 --- a/packages/ui/src/content/Icon.tsx +++ b/packages/ui/src/content/Icon.tsx @@ -8,4 +8,5 @@ export { AlertOctagon, Inbox, X, + RefreshCw, } from '@tamagui/lucide-icons' diff --git a/packages/ui/src/panels/Sheet.tsx b/packages/ui/src/panels/Sheet.tsx index 6ac49ef6..ca7f52c3 100644 --- a/packages/ui/src/panels/Sheet.tsx +++ b/packages/ui/src/panels/Sheet.tsx @@ -1,52 +1,35 @@ -import { Sheet as TSheet } from '@tamagui/sheet' -import { useState } from 'react' +import type { ForwardedRef } from 'react' -import { Button } from '../base' -import { ChevronDown, ChevronUp } from '../content' +import BottomSheet, { BottomSheetScrollView, BottomSheetBackdrop } from '@gorhom/bottom-sheet' +import { forwardRef } from 'react' +import { StyleSheet } from 'react-native' type Props = { - open: boolean - setOpen: React.Dispatch> - showChevron?: boolean - snapPoints?: number[] + snapPoints?: string[] children?: React.ReactNode } -export const Sheet = ({ open, setOpen, showChevron = false, snapPoints = [80], children }: Props) => { - const [position, setPosition] = useState(0) +export { BottomSheetScrollView } +export const Sheet = forwardRef(({ snapPoints = ['80%'], children }: Props, ref: ForwardedRef) => { return ( - <> - {showChevron && ( - : } - circular - onPress={() => setOpen((x) => !x)} + ( + )} - - - - - {children} - - - + index={-1} + snapPoints={snapPoints} + > + {children} + ) -} +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8bdc9ba2..9b649ad5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,6 +27,9 @@ importers: apps/funke: dependencies: + '@gorhom/bottom-sheet': + specifier: ^4.6.3 + version: 4.6.3(@types/react@18.2.79)(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) '@hyperledger/anoncreds-react-native': specifier: ^0.2.2 version: 0.2.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) @@ -95,7 +98,7 @@ importers: version: 3.0.6(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) expo-router: specifier: ~3.5.16 - version: 3.5.16(expo-constants@16.0.2(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(expo-linking@6.3.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(expo-modules-autolinking@1.11.1)(expo-status-bar@1.12.1)(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-screens@3.31.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)(typescript@5.3.3) + version: 3.5.16(43v2hg54mtm624tu4gmcsqcpna) expo-secure-store: specifier: ~13.0.1 version: 13.0.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) @@ -148,6 +151,9 @@ importers: apps/paradym: dependencies: + '@gorhom/bottom-sheet': + specifier: ^4.6.3 + version: 4.6.3(@types/react@18.2.79)(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) '@hyperledger/anoncreds-react-native': specifier: ^0.2.2 version: 0.2.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) @@ -216,7 +222,7 @@ importers: version: 3.0.6(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) expo-router: specifier: ~3.5.16 - version: 3.5.16(expo-constants@16.0.2(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(expo-linking@6.3.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(expo-modules-autolinking@1.11.1)(expo-status-bar@1.12.1)(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-screens@3.31.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)(typescript@5.3.3) + version: 3.5.16(43v2hg54mtm624tu4gmcsqcpna) expo-secure-store: specifier: ~13.0.1 version: 13.0.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) @@ -376,7 +382,7 @@ importers: version: 3.0.6(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) expo-router: specifier: ~3.5.16 - version: 3.5.16(expo-constants@16.0.2(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(expo-linking@6.3.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(expo-modules-autolinking@1.11.1)(expo-status-bar@1.12.1)(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-screens@3.31.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)(typescript@5.3.3) + version: 3.5.16(43v2hg54mtm624tu4gmcsqcpna) fast-text-encoding: specifier: ^1.0.6 version: 1.0.6 @@ -1701,6 +1707,27 @@ packages: '@floating-ui/utils@0.2.2': resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==} + '@gorhom/bottom-sheet@4.6.3': + resolution: {integrity: sha512-fSuSfbtoKsjmSeyz+tG2C0GtcEL7PS63iEXI23c9M+HeCT1IFK6ffmIa2pqyqB43L1jtkR+BWkpZwqXnN4H8xA==} + peerDependencies: + '@types/react': ~18.2.79 + '@types/react-native': '*' + react: 18.2.0 + react-native: '*' + react-native-gesture-handler: '>=1.10.1' + react-native-reanimated: '>=2.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-native': + optional: true + + '@gorhom/portal@1.0.14': + resolution: {integrity: sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A==} + peerDependencies: + react: 18.2.0 + react-native: '*' + '@graphql-typed-document-node/core@3.2.0': resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: @@ -5536,6 +5563,13 @@ packages: peerDependencies: react: 18.2.0 + react-native-reanimated@3.12.1: + resolution: {integrity: sha512-aXyV1ydKNA2u9fqRL8Z4fJ2RxNAusujNDdC4k0y9CawNEay5AGYgxhANqmjAabGRzHxsvfCXJC09lvbTRMHIFA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + react: 18.2.0 + react-native: '*' + react-native-safe-area-context@4.10.1: resolution: {integrity: sha512-w8tCuowDorUkPoWPXmhqosovBr33YsukkwYCDERZFHAxIkx6qBadYxfeoaJ91nCQKjkNzGrK5qhoNOeSIcYSpA==} peerDependencies: @@ -8602,6 +8636,23 @@ snapshots: '@floating-ui/utils@0.2.2': {} + '@gorhom/bottom-sheet@4.6.3(@types/react@18.2.79)(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)': + dependencies: + '@gorhom/portal': 1.0.14(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + invariant: 2.2.4 + react: 18.2.0 + react-native: 0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0) + react-native-gesture-handler: 2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + react-native-reanimated: 3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.79 + + '@gorhom/portal@1.0.14(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)': + dependencies: + nanoid: 3.3.7 + react: 18.2.0 + react-native: 0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0) + '@graphql-typed-document-node/core@3.2.0(graphql@15.8.0)': dependencies: graphql: 15.8.0 @@ -11739,7 +11790,7 @@ snapshots: expo: 51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)) optional: true - expo-router@3.5.16(expo-constants@16.0.2(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(expo-linking@6.3.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(expo-modules-autolinking@1.11.1)(expo-status-bar@1.12.1)(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-screens@3.31.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)(typescript@5.3.3): + expo-router@3.5.16(43v2hg54mtm624tu4gmcsqcpna): dependencies: '@expo/metro-runtime': 3.2.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0)) '@expo/server': 0.4.3(typescript@5.3.3) @@ -11756,6 +11807,8 @@ snapshots: react-native-safe-area-context: 4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) react-native-screens: 3.31.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) schema-utils: 4.2.0 + optionalDependencies: + react-native-reanimated: 3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) transitivePeerDependencies: - encoding - expo-modules-autolinking @@ -13576,6 +13629,22 @@ snapshots: react-fast-compare: 3.2.2 shallowequal: 1.1.0 + react-native-reanimated@3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0): + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7) + '@babel/preset-typescript': 7.24.7(@babel/core@7.24.7) + convert-source-map: 2.0.0 + invariant: 2.2.4 + react: 18.2.0 + react-native: 0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0) + transitivePeerDependencies: + - supports-color + react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 From 6e94049aa119e3ef8391131f39a80b58c7e4a9c0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 25 Jun 2024 18:12:54 +0200 Subject: [PATCH 2/6] fix: fixes from running Signed-off-by: Timo Glastra --- apps/funke/package.json | 1 + apps/paradym/app/_layout.tsx | 110 +++++++++--------- apps/paradym/package.json | 1 + .../hooks/useDidCommPresentationActions.ts | 26 ++--- .../PresentationNotificationScreen.tsx | 2 +- packages/ui/src/panels/Sheet.tsx | 50 ++++---- pnpm-lock.yaml | 70 +++++++++-- 7 files changed, 162 insertions(+), 98 deletions(-) diff --git a/apps/funke/package.json b/apps/funke/package.json index 707abc3b..2882b7b6 100644 --- a/apps/funke/package.json +++ b/apps/funke/package.json @@ -44,6 +44,7 @@ "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.16.2", "react-native-get-random-values": "~1.11.0", + "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.1", "react-native-screens": "~3.31.1", "react-native-svg": "15.2.0" diff --git a/apps/paradym/app/_layout.tsx b/apps/paradym/app/_layout.tsx index 5411aee7..d0963ab3 100644 --- a/apps/paradym/app/_layout.tsx +++ b/apps/paradym/app/_layout.tsx @@ -20,6 +20,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context' import { initializeAppAgent } from '.' import { mediatorDid } from './constants' +import { GestureHandlerRootView } from 'react-native-gesture-handler' void SplashScreen.preventAutoHideAsync() @@ -143,58 +144,63 @@ export default function HomeLayout() { return ( - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + ) } diff --git a/apps/paradym/package.json b/apps/paradym/package.json index d1fb52c1..fe5811a8 100644 --- a/apps/paradym/package.json +++ b/apps/paradym/package.json @@ -43,6 +43,7 @@ "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.16.2", "react-native-get-random-values": "~1.11.0", + "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.1", "react-native-screens": "~3.31.1", "react-native-svg": "15.2.0" diff --git a/packages/agent/src/hooks/useDidCommPresentationActions.ts b/packages/agent/src/hooks/useDidCommPresentationActions.ts index f5c9bb59..0a44ca9d 100644 --- a/packages/agent/src/hooks/useDidCommPresentationActions.ts +++ b/packages/agent/src/hooks/useDidCommPresentationActions.ts @@ -1,5 +1,4 @@ import type { - AnonCredsCredentialsForProofRequest, AnonCredsPredicateType, AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicate, @@ -24,14 +23,9 @@ export function useDidCommPresentationActions(proofExchangeId: string) { const proofExchange = useProofById(proofExchangeId) const connection = useConnectionById(proofExchange?.connectionId ?? '') - let formatKey: 'anoncreds' | 'indy' | undefined = undefined - let anonCredsCredentials: AnonCredsCredentialsForProofRequest | undefined - const { data } = useQuery({ queryKey: ['didCommPresentationSubmission', proofExchangeId], - queryFn: async (): Promise => { - formatKey = undefined - anonCredsCredentials = undefined + queryFn: async () => { const repository = agent.dependencyManager.resolve(CredentialRepository) const formatData = await agent.proofs.getFormatData(proofExchangeId) @@ -41,8 +35,9 @@ export function useDidCommPresentationActions(proofExchangeId: string) { proofRecordId: proofExchangeId, }) - formatKey = formatData.request?.anoncreds !== undefined ? 'anoncreds' : 'indy' - anonCredsCredentials = credentialsForRequest.proofFormats.anoncreds ?? credentialsForRequest.proofFormats.indy + const formatKey = formatData.request?.anoncreds !== undefined ? 'anoncreds' : 'indy' + const anonCredsCredentials = + credentialsForRequest.proofFormats.anoncreds ?? credentialsForRequest.proofFormats.indy if (!anonCredsCredentials || !proofRequest) { throw new CredoError('Invalid proof request.') } @@ -133,7 +128,7 @@ export function useDidCommPresentationActions(proofExchangeId: string) { submission.areAllSatisfied = submission.entries.every((entry) => entry.isSatisfied) - return submission + return { submission, formatKey, anonCredsCredentials } }, }) @@ -144,23 +139,24 @@ export function useDidCommPresentationActions(proofExchangeId: string) { undefined if (selectedCredentials && Object.keys(selectedCredentials).length > 0) { - if (!formatKey || !anonCredsCredentials) throw new Error('Unable to accept presentation without credentials') + if (!data?.formatKey || !data.anonCredsCredentials) + throw new Error('Unable to accept presentation without credentials') const selectedAttributes = Object.fromEntries( - Object.entries(anonCredsCredentials.attributes).map(([groupName, matches]) => [ + Object.entries(data.anonCredsCredentials.attributes).map(([groupName, matches]) => [ groupName, matches[selectedCredentials[groupName] ?? 0], ]) ) const selectedPredicates = Object.fromEntries( - Object.entries(anonCredsCredentials.predicates).map(([groupName, matches]) => [ + Object.entries(data.anonCredsCredentials.predicates).map(([groupName, matches]) => [ groupName, matches[selectedCredentials[groupName] ?? 0], ]) ) formatInput = { - [formatKey]: { + [data.formatKey]: { attributes: selectedAttributes, predicates: selectedPredicates, selfAttestedAttributes: {}, @@ -218,7 +214,7 @@ export function useDidCommPresentationActions(proofExchangeId: string) { acceptStatus, declineStatus, proofExchange, - submission: data, + submission: data?.submission, verifierName: connection?.theirLabel, } } diff --git a/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx b/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx index abb026ab..49e79574 100644 --- a/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx +++ b/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx @@ -152,7 +152,7 @@ export function PresentationNotificationScreen({ )} - + setChangeSubmissionCredentialIndex(-1)}> {currentSubmissionEntry?.credentials.map((c, credentialIndex) => ( diff --git a/packages/ui/src/panels/Sheet.tsx b/packages/ui/src/panels/Sheet.tsx index ca7f52c3..8a71fea7 100644 --- a/packages/ui/src/panels/Sheet.tsx +++ b/packages/ui/src/panels/Sheet.tsx @@ -7,29 +7,35 @@ import { StyleSheet } from 'react-native' type Props = { snapPoints?: string[] children?: React.ReactNode + onClose?: () => void } export { BottomSheetScrollView } -export const Sheet = forwardRef(({ snapPoints = ['80%'], children }: Props, ref: ForwardedRef) => { - return ( - ( - - )} - index={-1} - snapPoints={snapPoints} - > - {children} - - ) -}) +export const Sheet = forwardRef( + ({ snapPoints = ['80%'], children, onClose }: Props, ref: ForwardedRef) => { + return ( + ( + + )} + index={-1} + snapPoints={snapPoints} + > + {children} + + ) + } +) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b649ad5..d076e9a8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,7 +29,7 @@ importers: dependencies: '@gorhom/bottom-sheet': specifier: ^4.6.3 - version: 4.6.3(@types/react@18.2.79)(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + version: 4.6.3(@types/react@18.2.79)(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) '@hyperledger/anoncreds-react-native': specifier: ^0.2.2 version: 0.2.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) @@ -98,7 +98,7 @@ importers: version: 3.0.6(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) expo-router: specifier: ~3.5.16 - version: 3.5.16(43v2hg54mtm624tu4gmcsqcpna) + version: 3.5.16(yh3fnxcrfoi2lc6zcgkyb5qnya) expo-secure-store: specifier: ~13.0.1 version: 13.0.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) @@ -129,6 +129,9 @@ importers: react-native-get-random-values: specifier: ~1.11.0 version: 1.11.0(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0)) + react-native-reanimated: + specifier: ~3.10.1 + version: 3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) react-native-safe-area-context: specifier: 4.10.1 version: 4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) @@ -151,9 +154,6 @@ importers: apps/paradym: dependencies: - '@gorhom/bottom-sheet': - specifier: ^4.6.3 - version: 4.6.3(@types/react@18.2.79)(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) '@hyperledger/anoncreds-react-native': specifier: ^0.2.2 version: 0.2.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) @@ -222,7 +222,7 @@ importers: version: 3.0.6(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) expo-router: specifier: ~3.5.16 - version: 3.5.16(43v2hg54mtm624tu4gmcsqcpna) + version: 3.5.16(yh3fnxcrfoi2lc6zcgkyb5qnya) expo-secure-store: specifier: ~13.0.1 version: 13.0.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) @@ -253,6 +253,9 @@ importers: react-native-get-random-values: specifier: ~1.11.0 version: 1.11.0(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0)) + react-native-reanimated: + specifier: ~3.10.1 + version: 3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) react-native-safe-area-context: specifier: 4.10.1 version: 4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) @@ -5563,6 +5566,13 @@ packages: peerDependencies: react: 18.2.0 + react-native-reanimated@3.10.1: + resolution: {integrity: sha512-sfxg6vYphrDc/g4jf/7iJ7NRi+26z2+BszPmvmk0Vnrz6FL7HYljJqTf531F1x6tFmsf+FEAmuCtTUIXFLVo9w==} + peerDependencies: + '@babel/core': ^7.0.0-0 + react: 18.2.0 + react-native: '*' + react-native-reanimated@3.12.1: resolution: {integrity: sha512-aXyV1ydKNA2u9fqRL8Z4fJ2RxNAusujNDdC4k0y9CawNEay5AGYgxhANqmjAabGRzHxsvfCXJC09lvbTRMHIFA==} peerDependencies: @@ -8636,14 +8646,14 @@ snapshots: '@floating-ui/utils@0.2.2': {} - '@gorhom/bottom-sheet@4.6.3(@types/react@18.2.79)(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)': + '@gorhom/bottom-sheet@4.6.3(@types/react@18.2.79)(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)': dependencies: '@gorhom/portal': 1.0.14(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) invariant: 2.2.4 react: 18.2.0 react-native: 0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0) react-native-gesture-handler: 2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) - react-native-reanimated: 3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + react-native-reanimated: 3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) optionalDependencies: '@types/react': 18.2.79 @@ -11817,6 +11827,33 @@ snapshots: - supports-color - typescript + expo-router@3.5.16(yh3fnxcrfoi2lc6zcgkyb5qnya): + dependencies: + '@expo/metro-runtime': 3.2.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0)) + '@expo/server': 0.4.3(typescript@5.3.3) + '@radix-ui/react-slot': 1.0.1(react@18.2.0) + '@react-navigation/bottom-tabs': 6.5.20(@react-navigation/native@6.1.17(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-screens@3.31.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + '@react-navigation/native': 6.1.17(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + '@react-navigation/native-stack': 6.9.26(@react-navigation/native@6.1.17(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-screens@3.31.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + expo: 51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)) + expo-constants: 16.0.2(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) + expo-linking: 6.3.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) + expo-splash-screen: 0.27.5(expo-modules-autolinking@1.11.1)(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))) + expo-status-bar: 1.12.1 + react-native-helmet-async: 2.0.4(react@18.2.0) + react-native-safe-area-context: 4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + react-native-screens: 3.31.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + schema-utils: 4.2.0 + optionalDependencies: + react-native-reanimated: 3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + transitivePeerDependencies: + - encoding + - expo-modules-autolinking + - react + - react-native + - supports-color + - typescript + expo-secure-store@13.0.1(expo@51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))): dependencies: expo: 51.0.12(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)) @@ -13629,6 +13666,22 @@ snapshots: react-fast-compare: 3.2.2 shallowequal: 1.1.0 + react-native-reanimated@3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0): + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7) + '@babel/preset-typescript': 7.24.7(@babel/core@7.24.7) + convert-source-map: 2.0.0 + invariant: 2.2.4 + react: 18.2.0 + react-native: 0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0) + transitivePeerDependencies: + - supports-color + react-native-reanimated@3.12.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0): dependencies: '@babel/core': 7.24.7 @@ -13644,6 +13697,7 @@ snapshots: react-native: 0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0) transitivePeerDependencies: - supports-color + optional: true react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0): dependencies: From cf07459310e58129eb9b974db2f32535aa40c128 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 25 Jun 2024 18:13:15 +0200 Subject: [PATCH 3/6] style Signed-off-by: Timo Glastra --- apps/paradym/app/_layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/paradym/app/_layout.tsx b/apps/paradym/app/_layout.tsx index d0963ab3..36b4df25 100644 --- a/apps/paradym/app/_layout.tsx +++ b/apps/paradym/app/_layout.tsx @@ -19,8 +19,8 @@ import { useEffect, useState } from 'react' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { initializeAppAgent } from '.' -import { mediatorDid } from './constants' import { GestureHandlerRootView } from 'react-native-gesture-handler' +import { mediatorDid } from './constants' void SplashScreen.preventAutoHideAsync() From 35b6e34da7f07ed058f896eec229c2992f2c4792 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 26 Jun 2024 10:23:19 +0200 Subject: [PATCH 4/6] chore: disable selection in UI Signed-off-by: Timo Glastra --- .../components/PresentationNotificationScreen.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx b/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx index 49e79574..a8798c13 100644 --- a/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx +++ b/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx @@ -97,7 +97,8 @@ export function PresentationNotificationScreen({ bg="$white" gap="$2" borderColor={s.isSatisfied ? '$grey-300' : '$danger-500'} - onPress={s.credentials.length > 1 ? () => setChangeSubmissionCredentialIndex(i) : undefined} + // disable credential selection until we have better UX + // onPress={s.credentials.length > 1 ? () => setChangeSubmissionCredentialIndex(i) : undefined} pressStyle={{ backgroundColor: s.isSatisfied ? '$grey-100' : undefined }} > @@ -110,7 +111,8 @@ export function PresentationNotificationScreen({ bgColor={selectedCredential?.backgroundColor} /> - {s.credentials.length > 1 && } + {/* Disable credential selection until we have better UX */} + {/* {s.credentials.length > 1 && } */} {s.description && ( From fb1dfcf56275b76e1b025525e6a122d5d4e74011 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 13 Jul 2024 18:12:28 +0200 Subject: [PATCH 5/6] add id to crednetials Signed-off-by: Timo Glastra --- packages/agent/src/format/formatPresentation.ts | 2 ++ .../src/hooks/useDidCommPresentationActions.ts | 7 ++++--- packages/agent/src/invitation/handler.ts | 10 ++++++---- .../OpenIdPresentationNotificationScreen.tsx | 6 +++--- .../PresentationNotificationScreen.tsx | 16 +++++++--------- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/agent/src/format/formatPresentation.ts b/packages/agent/src/format/formatPresentation.ts index 6855d7f2..2b6b46b5 100644 --- a/packages/agent/src/format/formatPresentation.ts +++ b/packages/agent/src/format/formatPresentation.ts @@ -20,6 +20,7 @@ export interface FormattedSubmissionEntry { description?: string credentials: Array<{ + id: string credentialName: string issuerName?: string requestedAttributes?: string[] @@ -51,6 +52,7 @@ export function formatDifPexCredentialsForRequest( } return { + id: verifiableCredential.credentialRecord.id, credentialName: display.name, issuerName: display.issuer.name, requestedAttributes, diff --git a/packages/agent/src/hooks/useDidCommPresentationActions.ts b/packages/agent/src/hooks/useDidCommPresentationActions.ts index d6f32c35..03b502ed 100644 --- a/packages/agent/src/hooks/useDidCommPresentationActions.ts +++ b/packages/agent/src/hooks/useDidCommPresentationActions.ts @@ -141,6 +141,7 @@ export function useDidCommPresentationActions(proofExchangeId: string) { : undefined return { + id: match.credentialId, credentialName: credentialDisplayMetadata?.credentialName ?? 'Credential', isSatisfied: true, issuerName: credentialDisplayMetadata?.issuerName ?? 'Unknown', @@ -165,7 +166,7 @@ export function useDidCommPresentationActions(proofExchangeId: string) { const { mutateAsync: acceptMutateAsync, status: acceptStatus } = useMutation({ mutationKey: ['acceptDidCommPresentation', proofExchangeId], - mutationFn: async (selectedCredentials?: { [groupName: string]: number }) => { + mutationFn: async (selectedCredentials?: { [inputDescriptorId: string]: string }) => { let formatInput: { indy?: AnonCredsSelectedCredentials; anoncreds?: AnonCredsSelectedCredentials } | undefined = undefined @@ -176,8 +177,8 @@ export function useDidCommPresentationActions(proofExchangeId: string) { const selectedPredicates: Record = {} for (const [inputDescriptorId, entry] of Array.from(data.entries.entries())) { - const matchIndex = selectedCredentials[inputDescriptorId] ?? 0 - const match = entry.matches[matchIndex] + const credentialId = selectedCredentials[inputDescriptorId] + const match = entry.matches.find((match) => match.credentialId === credentialId) ?? entry.matches[0] for (const groupName of entry.groupNames.attributes) { selectedAttributes[groupName] = { diff --git a/packages/agent/src/invitation/handler.ts b/packages/agent/src/invitation/handler.ts index 3ac9835c..2b0e8a80 100644 --- a/packages/agent/src/invitation/handler.ts +++ b/packages/agent/src/invitation/handler.ts @@ -21,7 +21,6 @@ import { CredentialState, DidJwk, DidKey, - DifPresentationExchangeService, JwaSignatureAlgorithm, OutOfBandRepository, ProofEventTypes, @@ -231,7 +230,7 @@ export const shareProof = async ({ agent: FullAppAgent authorizationRequest: OpenId4VcSiopVerifiedAuthorizationRequest credentialsForRequest: DifPexCredentialsForRequest - selectedCredentials: { [inputDescriptorId: string]: number } + selectedCredentials: { [inputDescriptorId: string]: string } }) => { if (!credentialsForRequest.areRequirementsSatisfied) { throw new Error('Requirements from proof request are not satisfied') @@ -243,9 +242,12 @@ export const shareProof = async ({ const credentials = Object.fromEntries( credentialsForRequest.requirements.flatMap((requirement) => requirement.submissionEntry.map((entry) => { - const vcIndex = selectedCredentials[entry.inputDescriptorId] ?? 0 + const credentialId = selectedCredentials[entry.inputDescriptorId] + const credential = + entry.verifiableCredentials.find((vc) => vc.credentialRecord.id === credentialId) ?? + entry.verifiableCredentials[0] - return [entry.inputDescriptorId, [entry.verifiableCredentials[vcIndex].credentialRecord]] + return [entry.inputDescriptorId, [credential.credentialRecord]] }) ) ) diff --git a/packages/app/src/features/notifications/OpenIdPresentationNotificationScreen.tsx b/packages/app/src/features/notifications/OpenIdPresentationNotificationScreen.tsx index 1679c903..9eacb599 100644 --- a/packages/app/src/features/notifications/OpenIdPresentationNotificationScreen.tsx +++ b/packages/app/src/features/notifications/OpenIdPresentationNotificationScreen.tsx @@ -31,7 +31,7 @@ export function OpenIdPresentationNotificationScreen() { ) const [selectedCredentials, setSelectedCredentials] = useState<{ - [inputDescriptorId: string]: number + [inputDescriptorId: string]: string }>({}) const pushToWallet = useCallback(() => { @@ -101,10 +101,10 @@ export function OpenIdPresentationNotificationScreen() { isAccepting={isSharing} verifierName={credentialsForRequest.verifierHostName} selectedCredentials={selectedCredentials} - onSelectCredentialForInputDescriptor={(inputDescriptorId: string, index: number) => + onSelectCredentialForInputDescriptor={(inputDescriptorId: string, credentialId: string) => setSelectedCredentials((selectedCredentials) => ({ ...selectedCredentials, - [inputDescriptorId]: index, + [inputDescriptorId]: credentialId, })) } /> diff --git a/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx b/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx index a8798c13..1a436525 100644 --- a/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx +++ b/packages/app/src/features/notifications/components/PresentationNotificationScreen.tsx @@ -26,8 +26,8 @@ interface PresentationNotificationScreenProps { onAccept: () => void onDecline: () => void verifierName?: string - selectedCredentials: { [inputDescriptorId: string]: number } - onSelectCredentialForInputDescriptor: (inputDescriptorId: string, index: number) => void + selectedCredentials: { [inputDescriptorId: string]: string } + onSelectCredentialForInputDescriptor: (inputDescriptorId: string, credentialId: string) => void } export function PresentationNotificationScreen({ @@ -86,11 +86,11 @@ export function PresentationNotificationScreen({ {submission.entries.map((s, i) => { - const selectedCredentialIndex = selectedCredentials[s.inputDescriptorId] ?? 0 - const selectedCredential = s.credentials[selectedCredentialIndex] + const selectedCredentialId = selectedCredentials[s.inputDescriptorId] + const selectedCredential = s.credentials.find((c) => c.id === selectedCredentialId) ?? s.credentials[0] return ( - + ( { - onSelectCredentialForInputDescriptor(currentSubmissionEntry.inputDescriptorId, credentialIndex) + onSelectCredentialForInputDescriptor(currentSubmissionEntry.inputDescriptorId, c.id) setChangeSubmissionCredentialIndex(-1) }} - // The index is stable enough here - // biome-ignore lint/suspicious/noArrayIndexKey: - key={credentialIndex} + key={c.id} issuer={c.issuerName} name={c.credentialName} hideBorder={credentialIndex === currentSubmissionEntry.credentials.length - 1} From 2d0609c846d729a207d14a7ca2ca37b8b72d74ef Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 13 Jul 2024 18:29:17 +0200 Subject: [PATCH 6/6] fix typescript issue Signed-off-by: Timo Glastra --- .../notifications/DidCommPresentationNotificationScreen.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/src/features/notifications/DidCommPresentationNotificationScreen.tsx b/packages/app/src/features/notifications/DidCommPresentationNotificationScreen.tsx index 3fd22953..2d55cf96 100644 --- a/packages/app/src/features/notifications/DidCommPresentationNotificationScreen.tsx +++ b/packages/app/src/features/notifications/DidCommPresentationNotificationScreen.tsx @@ -20,7 +20,7 @@ export function DidCommPresentationNotificationScreen({ proofExchangeId }: DidCo useDidCommPresentationActions(proofExchangeId) const [selectedCredentials, setSelectedCredentials] = useState<{ - [groupName: string]: number + [inputDescriptorId: string]: string }>({}) const pushToWallet = () => { @@ -63,10 +63,10 @@ export function DidCommPresentationNotificationScreen({ proofExchangeId }: DidCo isAccepting={acceptStatus !== 'idle'} verifierName={verifierName} selectedCredentials={selectedCredentials} - onSelectCredentialForInputDescriptor={(groupName: string, vcIndex: number) => + onSelectCredentialForInputDescriptor={(groupName: string, credentialId: string) => setSelectedCredentials((selectedCredentials) => ({ ...selectedCredentials, - [groupName]: vcIndex, + [groupName]: credentialId, })) } />