diff --git a/packages/agent/src/hooks/index.ts b/packages/agent/src/hooks/index.ts index 0d2173b3..cae88a71 100644 --- a/packages/agent/src/hooks/index.ts +++ b/packages/agent/src/hooks/index.ts @@ -1,5 +1,5 @@ export * from './useCredentialsForDisplay' export * from './useCredentialForDisplayById' -export * from './useAcceptDidCommCredential' -export * from './useAcceptDidCommPresentation' +export * from './useDidCommCredentialActions' +export * from './useDidCommPresentationActions' export * from './useInboxNotifications' diff --git a/packages/agent/src/hooks/useAcceptDidCommCredential.ts b/packages/agent/src/hooks/useDidCommCredentialActions.ts similarity index 68% rename from packages/agent/src/hooks/useAcceptDidCommCredential.ts rename to packages/agent/src/hooks/useDidCommCredentialActions.ts index 632d9f3a..b5380d52 100644 --- a/packages/agent/src/hooks/useAcceptDidCommCredential.ts +++ b/packages/agent/src/hooks/useDidCommCredentialActions.ts @@ -36,13 +36,13 @@ function useOfferAttributes(credentialExchangeId: string) { } } -export function useAcceptDidCommCredential(credentialExchangeId: string) { +export function useDidCommCredentialActions(credentialExchangeId: string) { const { agent } = useAgent() const credentialExchange = useCredentialById(credentialExchangeId) const { data } = useOfferAttributes(credentialExchangeId) - const { mutateAsync, status } = useMutation({ + const { mutateAsync: acceptCredentialMutation, status: acceptStatus } = useMutation({ mutationKey: ['acceptDidCommCredential', credentialExchangeId], mutationFn: async () => { const credentialDone$ = agent.events @@ -68,13 +68,41 @@ export function useAcceptDidCommCredential(credentialExchangeId: string) { }, }) + const { mutateAsync: declineCredentialMutation, status: declineStatus } = useMutation({ + mutationKey: ['declineDidCommCredential', credentialExchangeId], + mutationFn: async () => { + const credentialDone$ = agent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .pipe( + // Correct record with id and state + filter( + (event) => + event.payload.credentialRecord.id === credentialExchangeId && + event.payload.credentialRecord.state === CredentialState.Declined + ), + // 10 seconds to complete exchange + timeout(10000), + first() + ) + + const credentialDonePromise = firstValueFrom(credentialDone$) + + await agent.credentials.declineOffer(credentialExchangeId, { + sendProblemReport: true, + }) + await credentialDonePromise + }, + }) + const didcommDisplayMetadata = credentialExchange ? getDidCommCredentialExchangeDisplayMetadata(credentialExchange) : undefined return { - acceptCredential: mutateAsync, - status, + acceptCredential: acceptCredentialMutation, + declineCredential: declineCredentialMutation, + acceptStatus, + declineStatus, credentialExchange, display: { issuer: { diff --git a/packages/agent/src/hooks/useAcceptDidCommPresentation.ts b/packages/agent/src/hooks/useDidCommPresentationActions.ts similarity index 85% rename from packages/agent/src/hooks/useAcceptDidCommPresentation.ts rename to packages/agent/src/hooks/useDidCommPresentationActions.ts index 886213c5..e23bf8dd 100644 --- a/packages/agent/src/hooks/useAcceptDidCommPresentation.ts +++ b/packages/agent/src/hooks/useDidCommPresentationActions.ts @@ -16,7 +16,7 @@ import { filter, first, timeout } from 'rxjs/operators' import { useAgent } from '../agent' import { getDidCommCredentialExchangeDisplayMetadata } from '../didcomm/metadata' -export function useAcceptDidCommPresentation(proofExchangeId: string) { +export function useDidCommPresentationActions(proofExchangeId: string) { const { agent } = useAgent() const proofExchange = useProofById(proofExchangeId) @@ -131,7 +131,7 @@ export function useAcceptDidCommPresentation(proofExchangeId: string) { }, }) - const { mutateAsync, status } = useMutation({ + const { mutateAsync: acceptMutateAsync, status: acceptStatus } = useMutation({ mutationKey: ['acceptDidCommPresentation', proofExchangeId], mutationFn: async () => { const presentationDone$ = agent.events @@ -157,9 +157,34 @@ export function useAcceptDidCommPresentation(proofExchangeId: string) { }, }) + const { mutateAsync: declineMutateAsync, status: declineStatus } = useMutation({ + mutationKey: ['declineDidCommPresentation', proofExchangeId], + mutationFn: async () => { + const presentationDeclined$ = agent.events + .observable(ProofEventTypes.ProofStateChanged) + .pipe( + // Correct record with id and state + filter( + (event) => + event.payload.proofRecord.id === proofExchangeId && + [ProofState.Declined].includes(event.payload.proofRecord.state) + ), + // 10 seconds to complete exchange + timeout(10000), + first() + ) + + const presentationDeclinePromise = firstValueFrom(presentationDeclined$) + await agent.proofs.declineRequest({ proofRecordId: proofExchangeId, sendProblemReport: true }) + await presentationDeclinePromise + }, + }) + return { - acceptPresentation: mutateAsync, - status, + acceptPresentation: acceptMutateAsync, + declinePresentation: declineMutateAsync, + acceptStatus, + declineStatus, proofExchange, submission: data, verifierName: connection?.theirLabel, diff --git a/packages/app/features/notifications/DidCommCredentialNotificationScreen.tsx b/packages/app/features/notifications/DidCommCredentialNotificationScreen.tsx index a2724c89..f33f7fa8 100644 --- a/packages/app/features/notifications/DidCommCredentialNotificationScreen.tsx +++ b/packages/app/features/notifications/DidCommCredentialNotificationScreen.tsx @@ -1,4 +1,4 @@ -import { useAcceptDidCommCredential, useAgent } from '@internal/agent' +import { useDidCommCredentialActions, useAgent } from '@internal/agent' import { useToastController } from '@internal/ui' import React from 'react' import { useRouter } from 'solito/router' @@ -18,8 +18,14 @@ export function DidCommCredentialNotificationScreen({ const router = useRouter() const toast = useToastController() - const { acceptCredential, status, credentialExchange, attributes, display } = - useAcceptDidCommCredential(credentialExchangeId) + const { + acceptCredential, + acceptStatus, + declineCredential, + credentialExchange, + attributes, + display, + } = useDidCommCredentialActions(credentialExchangeId) const pushToWallet = () => { router.back() @@ -44,7 +50,9 @@ export function DidCommCredentialNotificationScreen({ } const onCredentialDecline = () => { - void agent.credentials.deleteById(credentialExchange.id) + declineCredential().finally(() => { + void agent.credentials.deleteById(credentialExchange.id) + }) toast.show('Credential has been declined.') pushToWallet() @@ -59,7 +67,7 @@ export function DidCommCredentialNotificationScreen({ }} onDecline={onCredentialDecline} // If state is not idle, it means we have pressed accept - isAccepting={status !== 'idle'} + isAccepting={acceptStatus !== 'idle'} /> ) } diff --git a/packages/app/features/notifications/DidCommPresentationNotificationScreen.tsx b/packages/app/features/notifications/DidCommPresentationNotificationScreen.tsx index 7ef87413..4a38846d 100644 --- a/packages/app/features/notifications/DidCommPresentationNotificationScreen.tsx +++ b/packages/app/features/notifications/DidCommPresentationNotificationScreen.tsx @@ -1,4 +1,4 @@ -import { useAcceptDidCommPresentation, useAgent } from '@internal/agent' +import { useDidCommPresentationActions, useAgent } from '@internal/agent' import { useToastController } from '@internal/ui' import React from 'react' import { useRouter } from 'solito/router' @@ -18,8 +18,14 @@ export function DidCommPresentationNotificationScreen({ const router = useRouter() const toast = useToastController() - const { acceptPresentation, proofExchange, status, submission, verifierName } = - useAcceptDidCommPresentation(proofExchangeId) + const { + acceptPresentation, + declinePresentation, + proofExchange, + acceptStatus, + submission, + verifierName, + } = useDidCommPresentationActions(proofExchangeId) const pushToWallet = () => { router.back() @@ -44,7 +50,9 @@ export function DidCommPresentationNotificationScreen({ } const onProofDecline = () => { - void agent.proofs.deleteById(proofExchange.id) + declinePresentation().finally(() => { + void agent.proofs.deleteById(proofExchange.id) + }) toast.show('Information request has been declined.') pushToWallet() @@ -56,7 +64,7 @@ export function DidCommPresentationNotificationScreen({ onDecline={onProofDecline} submission={submission} // If state is not idle, it means we have pressed accept - isAccepting={status !== 'idle'} + isAccepting={acceptStatus !== 'idle'} verifierName={verifierName} /> )