Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: select alternative credentials for proof #978

Merged
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c6c7a9d
change credential functionality working
wadeking98 Sep 21, 2023
0738ffa
Merge branch 'main' of https://github.com/hyperledger/aries-mobile-ag…
wadeking98 Sep 21, 2023
481dfba
resolved merge conflicts
wadeking98 Sep 22, 2023
577093d
working with predicate display
wadeking98 Sep 22, 2023
9e825dc
Merge branch 'main' of https://github.com/hyperledger/aries-mobile-ag…
wadeking98 Sep 25, 2023
d735474
fixed nav issue from chat screen
wadeking98 Sep 25, 2023
18eee54
Merge branch 'main' into feat-select-proof-cred
wadeking98 Sep 25, 2023
409ed50
fixed linting
wadeking98 Sep 25, 2023
599aa22
updated tests
wadeking98 Sep 25, 2023
f7af96b
updated tests
wadeking98 Sep 25, 2023
c725262
updated typedefs
wadeking98 Sep 26, 2023
2edea52
code factoring changes
wadeking98 Sep 26, 2023
251a17e
remove unused selectedCreds from proof screen
wadeking98 Sep 26, 2023
8e10b2b
updated PB lang index
wadeking98 Sep 26, 2023
39a432a
Merge branch 'main' into feat-select-proof-cred
wadeking98 Sep 26, 2023
9a7b448
switch to pressable
wadeking98 Sep 26, 2023
82cac2e
Merge branch 'feat-select-proof-cred' of https://github.com/wadeking9…
wadeking98 Sep 26, 2023
fab2a25
fixed ui bug on cred card
wadeking98 Sep 27, 2023
8d704ae
support for rev interval in other parts of proof
wadeking98 Sep 29, 2023
a092103
Merge branch 'main' of https://github.com/hyperledger/aries-mobile-ag…
wadeking98 Sep 29, 2023
657b24a
updated after react upgrade
wadeking98 Sep 29, 2023
50cbbc5
fixed missing credential display
wadeking98 Oct 3, 2023
0a1fbca
Merge branch 'main' into feat-select-proof-cred
wadeking98 Oct 3, 2023
5628ff2
Merge branch 'main' into feat-select-proof-cred
wadeking98 Oct 4, 2023
1b2208d
resolved merge conflicts
wadeking98 Oct 19, 2023
d9f27ce
Merge branch 'main' into feat-select-proof-cred
wadeking98 Oct 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/legacy/core/App/components/misc/CredentialCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ interface CredentialCardProps {
displayItems?: (Attribute | Predicate)[]
existsInWallet?: boolean
satisfiedPredicates?: boolean
hasAltCredentials?: boolean
handleAltCredChange?: () => void
}

const CredentialCard: React.FC<CredentialCardProps> = ({
Expand All @@ -32,6 +34,8 @@ const CredentialCard: React.FC<CredentialCardProps> = ({
credName,
existsInWallet,
satisfiedPredicates,
hasAltCredentials,
handleAltCredChange,
style = {},
onPress = undefined,
}) => {
Expand All @@ -50,6 +54,8 @@ const CredentialCard: React.FC<CredentialCardProps> = ({
credDefId={credDefId}
schemaId={schemaId}
credential={credential}
handleAltCredChange={handleAltCredChange}
hasAltCredentials={hasAltCredentials}
proof
elevated
></CredentialCard11>
Expand Down
47 changes: 46 additions & 1 deletion packages/legacy/core/App/components/misc/CredentialCard11.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ interface CredentialCard11Props {
credDefId?: string
schemaId?: string
proof?: boolean
hasAltCredentials?: boolean
handleAltCredChange?: () => void
}

/*
Expand Down Expand Up @@ -73,6 +75,8 @@ const CredentialCard11: React.FC<CredentialCard11Props> = ({
credDefId,
schemaId,
proof,
hasAltCredentials,
handleAltCredChange,
}) => {
const { width } = useWindowDimensions()
const borderRadius = 10
Expand Down Expand Up @@ -191,6 +195,22 @@ const CredentialCard11: React.FC<CredentialCard11Props> = ({
fontSize: 22,
transform: [{ rotate: '-30deg' }],
},
selectedCred: {
borderWidth: 5,
borderRadius: 15,
borderColor: ColorPallet.semantic.focus,
},
seperator: {
width: '100%',
height: 2,
marginVertical: 10,
backgroundColor: ColorPallet.grayscale.lightGrey,
},
credActionText: {
fontSize: 20,
fontWeight: 'bold',
color: ColorPallet.brand.link,
},
})

const parseAttribute = (item: (Attribute & Predicate) | undefined) => {
Expand Down Expand Up @@ -428,6 +448,26 @@ const CredentialCard11: React.FC<CredentialCard11Props> = ({
renderItem={({ item }) => {
return renderCardAttribute(item as Attribute & Predicate)
}}
ListFooterComponent={
hasAltCredentials ? (
<View style={{ width: '125%' }}>
<View style={styles.seperator}></View>
<View>
<TouchableOpacity
onPress={handleAltCredChange}
testID={testIdWithKey('changeCredential')}
style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}
>
<Text style={styles.credActionText}>{t('ProofRequest.ChangeCredential')}</Text>
<Icon
style={{ ...styles.credActionText, fontSize: styles.credActionText.fontSize + 5 }}
name="chevron-right"
></Icon>
</TouchableOpacity>
</View>
</View>
) : null
}
/>
</View>
</View>
Expand Down Expand Up @@ -538,7 +578,12 @@ const CredentialCard11: React.FC<CredentialCard11Props> = ({
}
return overlay.bundle ? (
<View
style={[styles.container, style, { elevation: elevated ? 5 : 0, overflow: 'hidden' }]}
style={[
styles.container,
style,
{ elevation: elevated ? 5 : 0, overflow: 'hidden' },
hasAltCredentials ? styles.selectedCred : undefined,
]}
onLayout={(event) => {
setDimensions({ cardHeight: event.nativeEvent.layout.height, cardWidth: event.nativeEvent.layout.width })
}}
Expand Down
18 changes: 17 additions & 1 deletion packages/legacy/core/App/hooks/proofs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ProofExchangeRecord } from '@aries-framework/core'
import { useProofs } from '@aries-framework/react-hooks'
import { useAgent, useCredentials, useProofById, useProofs } from '@aries-framework/react-hooks'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'

import { retrieveCredentialsForProof } from '../utils/helpers'

export const useProofsByConnectionId = (connectionId: string): ProofExchangeRecord[] => {
const { records: proofs } = useProofs()
Expand All @@ -9,3 +12,16 @@ export const useProofsByConnectionId = (connectionId: string): ProofExchangeReco
[proofs, connectionId]
)
}

export const getAllCredentialsForProof = (proofId: string) => {
wadeking98 marked this conversation as resolved.
Show resolved Hide resolved
const { t } = useTranslation()
const { agent } = useAgent()
const fullCredentials = useCredentials().records
const proof = useProofById(proofId)
return useMemo(() => {
if (!proof || !agent) {
return
}
return retrieveCredentialsForProof(agent, proof, fullCredentials, t)
}, [proofId])
}
3 changes: 3 additions & 0 deletions packages/legacy/core/App/localization/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,10 @@ const translation = {
"ProofRequest": "Proof Request",
"RequestProcessing": "Just a moment...",
"OfferDelay": "Offer delay",
"ChangeCredential": "Change credential",
"RejectThisProof?": "Reject this Proof Request?",
"DeclineThisProof?": "Decline this Proof Request?",
"MultipleCredentials": "You have multiple credentials to choose from:",
"AcceptingProof": "Accepting Proof",
"SuccessfullyAcceptedProof": "Successfully Accepted Proof",
"SensitiveInformation": "This request is asking for sensitive information.",
Expand Down Expand Up @@ -509,6 +511,7 @@ const translation = {
"CredentialDetails": "Credential Details",
"Notifications": "Notifications",
"CredentialOffer": "Credential Offer",
"ProofChangeCredential": "Choose a credential",
"ProofRequest": "Proof Request",
"ProofRequestDetails": "Proof Request Details",
"ProofRequestAttributeDetails": "Proof Request Attribute Details",
Expand Down
3 changes: 3 additions & 0 deletions packages/legacy/core/App/localization/fr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,10 @@ const translation = {
"ProofRequest": "Demande de preuve",
"RequestProcessing": "Juste un instant...",
"OfferDelay": "Retard de l'offre",
"ChangeCredential": "Change credential (FR)",
"RejectThisProof?": "Rejeter cette preuve?",
"AcceptingProof": "Acceptation de la preuve",
"MultipleCredentials": "You have multiple credentials to choose from: (FR)",
"SuccessfullyAcceptedProof": "Preuve acceptée avec succès",
"SensitiveInformation": "This request is asking for sensitive information. (FR)",
"RejectingProof": "Rejet de la preuve",
Expand Down Expand Up @@ -498,6 +500,7 @@ const translation = {
"CredentialDetails": "Détails des justificatifs",
"Notifications": "Notifications",
"CredentialOffer": "Offre de justificatif",
"ProofChangeCredential":"Choose a credential (FR)",
"ProofRequest": "Demande de preuve",
"ProofRequestAttributeDetails": "Détails des attributs de la demande de preuve",
"ProofDetails": "Détails de la preuve",
Expand Down
3 changes: 3 additions & 0 deletions packages/legacy/core/App/localization/pt-br/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,11 @@ const translation = {
"ProofRequest": "Requisição de Prova",
"RequestProcessing": "Só um momento...",
"OfferDelay": "Atrasar oferta",
"ChangeCredential": "Escolher credencial",
"RejectThisProof?": "Rejeitar esta Requisição de Prova?",
"DeclineThisProof?": "Recusar esta Requisição de Prova?",
"AcceptingProof": "Aceitando Prova",
"MultipleCredentials": "Você tem múltiplas credenciais para escolher:",
"SuccessfullyAcceptedProof": "Prova Aceita com Sucesso",
"SensitiveInformation": "This request is asking for sensitive information. (PB)",
"ProofRequestNotFound": "Requisição de Prova não encontrada.",
Expand Down Expand Up @@ -482,6 +484,7 @@ const translation = {
"Notifications": "Notificações",
"CredentialOffer": "Oferta de Credencial",
"ProofRequest": "Requisição de Prova",
"ProofChangeCredential":"Escolha uma credencial",
"ProofRequestDetails": "Detalhes Da Solicitação De Comprovação",
"ProofRequestAttributeDetails": "Atributos de Requisição de Prova",
"ProofDetails": "Detalhes da prova",
Expand Down
6 changes: 6 additions & 0 deletions packages/legacy/core/App/navigators/ProofRequestStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import HeaderButton, { ButtonLocation } from '../components/buttons/HeaderButton
import HeaderRightHome from '../components/buttons/HeaderHome'
import { useTheme } from '../contexts/theme'
import ListProofRequests from '../screens/ListProofRequests'
import ProofChangeCredential from '../screens/ProofChangeCredential'
import ProofDetails from '../screens/ProofDetails'
import ProofRequestDetails from '../screens/ProofRequestDetails'
import ProofRequestUsageHistory from '../screens/ProofRequestUsageHistory'
Expand Down Expand Up @@ -35,6 +36,11 @@ const ProofRequestStack: React.FC = () => {
title: '',
})}
/>
<Stack.Screen
name={Screens.ProofChangeCredential}
component={ProofChangeCredential}
options={{ title: t('Screens.ProofChangeCredential') }}
></Stack.Screen>
<Stack.Screen
name={Screens.ProofRequesting}
component={ProofRequesting}
Expand Down
176 changes: 176 additions & 0 deletions packages/legacy/core/App/screens/ProofChangeCredential.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import {
AnonCredsCredentialsForProofRequest,
AnonCredsRequestedAttributeMatch,
AnonCredsRequestedPredicateMatch,
} from '@aries-framework/anoncreds'
import { StackScreenProps } from '@react-navigation/stack'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { DeviceEventEmitter, FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'

import RecordLoading from '../components/animated/RecordLoading'
import { CredentialCard } from '../components/misc'
import { EventTypes } from '../constants'
import { useTheme } from '../contexts/theme'
import { getAllCredentialsForProof } from '../hooks/proofs'
import { BifoldError } from '../types/error'
import { ProofRequestsStackParams, Screens } from '../types/navigators'
import { ProofCredentialItems } from '../types/proof-items'
import { Fields, evaluatePredicates } from '../utils/helpers'
import { testIdWithKey } from '../utils/testable'

type ProofChangeProps = StackScreenProps<ProofRequestsStackParams, Screens.ProofChangeCredential>

const ProofChangeCredential: React.FC<ProofChangeProps> = ({ route, navigation }) => {
if (!route?.params) {
throw new Error('Change credential route params were not set properly')
}
const proofId = route.params.proofId
const selectedCred = route.params.selectedCred
const altCredentials = route.params.altCredentials
const onCredChange = route.params.onCredChange
const { ColorPallet, TextTheme } = useTheme()
const { t } = useTranslation()
const [loading, setLoading] = useState(false)
const [proofItems, setProofItems] = useState<ProofCredentialItems[]>([])
const [retrievedCredentials, setRetrievedCredentials] = useState<AnonCredsCredentialsForProofRequest>()
const credProofPromise = getAllCredentialsForProof(proofId)
const styles = StyleSheet.create({
pageContainer: {
flex: 1,
},
pageMargin: {
marginHorizontal: 20,
},
cardLoading: {
backgroundColor: ColorPallet.brand.secondaryBackground,
flex: 1,
flexGrow: 1,
marginVertical: 35,
borderRadius: 15,
paddingHorizontal: 10,
},
selectedCred: {
borderWidth: 5,
borderRadius: 15,
borderColor: ColorPallet.semantic.focus,
},
})

const getCredentialsFields = (): Fields => ({
...retrievedCredentials?.attributes,
...retrievedCredentials?.predicates,
})

useEffect(() => {
setLoading(true)

credProofPromise
?.then((value) => {
if (value) {
const { groupedProof, retrievedCredentials } = value
setLoading(false)
const activeCreds = groupedProof.filter((proof) => altCredentials.includes(proof.credId))
const credList = activeCreds.map((cred) => cred.credId)
const formatCredentials = (
retrievedItems: Record<string, (AnonCredsRequestedAttributeMatch | AnonCredsRequestedPredicateMatch)[]>
) => {
return Object.keys(retrievedItems)
.map((key) => {
return {
[key]: retrievedItems[key].filter((attr) => credList.includes(attr.credentialId)),
}
})
.reduce((prev, curr) => {
return {
...prev,
...curr,
}
}, {})
}
const selectRetrievedCredentials: AnonCredsCredentialsForProofRequest | undefined = retrievedCredentials
? {
...retrievedCredentials,
attributes: formatCredentials(retrievedCredentials.attributes) as Record<
string,
AnonCredsRequestedAttributeMatch[]
>,
predicates: formatCredentials(retrievedCredentials.predicates) as Record<
string,
AnonCredsRequestedPredicateMatch[]
>,
}
: undefined
setRetrievedCredentials(selectRetrievedCredentials)
setProofItems(activeCreds)
}
})
.catch((err: unknown) => {
const error = new BifoldError(
t('Error.Title1026'),
t('Error.Message1026'),
(err as Error)?.message ?? err,
1026
)
DeviceEventEmitter.emit(EventTypes.ERROR_ADDED, error)
})
}, [])

const listHeader = () => {
return (
<View style={{ ...styles.pageMargin, marginVertical: 20 }}>
{loading ? (
<View style={styles.cardLoading}>
<RecordLoading />
</View>
) : (
<Text style={TextTheme.normal}>{t('ProofRequest.MultipleCredentials')}</Text>
)}
</View>
)
}

const changeCred = (credId: string) => {
onCredChange(credId)
navigation.goBack()
}
const hasSatisfiedPredicates = (fields: Fields, credId?: string) =>
proofItems.flatMap((item) => evaluatePredicates(fields, credId)(item)).every((p) => p.satisfied)

return (
<SafeAreaView style={styles.pageContainer} edges={['bottom', 'left', 'right']}>
<FlatList
data={proofItems}
ListHeaderComponent={listHeader}
renderItem={({ item }) => {
return (
<View style={styles.pageMargin}>
<TouchableOpacity
wadeking98 marked this conversation as resolved.
Show resolved Hide resolved
testID={testIdWithKey(`select:${item.credId}`)}
onPress={() => changeCred(item.credId ?? '')}
style={[item.credId === selectedCred ? styles.selectedCred : undefined, { marginBottom: 10 }]}
>
<CredentialCard
credential={item.credExchangeRecord}
credDefId={item.credDefId}
schemaId={item.schemaId}
displayItems={[
...(item.attributes ?? []),
...evaluatePredicates(getCredentialsFields(), item.credId)(item),
]}
credName={item.credName}
existsInWallet={true}
satisfiedPredicates={hasSatisfiedPredicates(getCredentialsFields(), item.credId)}
proof={true}
></CredentialCard>
</TouchableOpacity>
</View>
)
}}
></FlatList>
</SafeAreaView>
)
}

export default ProofChangeCredential
Loading