diff --git a/app/src/components/AddCredentialSlider.tsx b/app/src/components/AddCredentialSlider.tsx index a14baedb..4f867dcb 100644 --- a/app/src/components/AddCredentialSlider.tsx +++ b/app/src/components/AddCredentialSlider.tsx @@ -1,38 +1,26 @@ import { CredentialMetadataKeys, CredentialState } from '@aries-framework/core' -import { useAgent, useCredentialByState } from '@aries-framework/react-hooks' +import { useCredentialByState } from '@aries-framework/react-hooks' import { useNavigation } from '@react-navigation/core' -import { useTheme, useStore, Screens, Stacks } from 'aries-bifold' +import { useTheme, Screens, Stacks } from 'aries-bifold' import React, { useEffect, useState, useCallback } from 'react' -import { useTranslation } from 'react-i18next' import { DeviceEventEmitter, Modal, StyleSheet, Text, TouchableOpacity, View } from 'react-native' import Icon from 'react-native-vector-icons/MaterialCommunityIcons' import { BCWalletEventTypes } from '../events/eventTypes' -import { showBCIDSelector, startFlow } from '../helpers/BCIDHelper' -import { BCState } from '../store' - -import LoadingIcon from './LoadingIcon' +import { showBCIDSelector } from '../helpers/BCIDHelper' const AddCredentialSlider: React.FC = () => { const { ColorPallet, TextTheme } = useTheme() - const { agent } = useAgent() - const { t } = useTranslation() - const [store] = useStore() - const [showGetFoundationCredential, setShowGetFoundationCredential] = useState(false) - const [addCredentialPressed, setAddCredentialPressed] = useState(false) - const [workflowInFlight, setWorkflowInFlight] = useState(false) - const [workflowConnectionId, setWorkflowConnectionId] = useState() + const navigation = useNavigation() - const offers = useCredentialByState(CredentialState.OfferReceived) + const [addCredentialPressed, setAddCredentialPressed] = useState(false) + const [showGetFoundationCredential, setShowGetFoundationCredential] = useState(false) const credentials = [ ...useCredentialByState(CredentialState.CredentialReceived), ...useCredentialByState(CredentialState.Done), ] - const navigation = useNavigation() - const [canUseLSBCredential] = useState(true) - const styles = StyleSheet.create({ centeredView: { marginTop: 'auto', @@ -79,39 +67,25 @@ const AddCredentialSlider: React.FC = () => { DeviceEventEmitter.emit(BCWalletEventTypes.ADD_CREDENTIAL_PRESSED, false) }, []) - const goToHomeScreen = (credentialId?: string) => { - navigation.getParent()?.navigate(Stacks.NotificationStack, { - screen: Screens.CredentialOffer, - params: { credentialId }, - }) - } - const goToScanScreen = useCallback(() => { deactivateSlider() navigation.getParent()?.navigate(Stacks.ConnectStack, { screen: Screens.Scan }) }, []) - const onBCIDPress = useCallback(() => { - setWorkflowInFlight(true) - startFlow(agent!, store, setWorkflowInFlight, t, (connectionId) => setWorkflowConnectionId(connectionId)) - }, [store]) - - useEffect(() => { - for (const credential of offers) { - if (credential.state == CredentialState.OfferReceived && credential.connectionId === workflowConnectionId) { - goToHomeScreen(credential.id) - deactivateSlider() - } - } - }, [offers, workflowConnectionId]) + const goToPersonCredentialScreen = useCallback(() => { + deactivateSlider() + navigation.getParent()?.navigate(Stacks.NotificationStack, { + screen: Screens.CustomNotification, + }) + }, []) useEffect(() => { const credentialDefinitionIDs = credentials.map( (c) => c.metadata.data[CredentialMetadataKeys.IndyCredential].credentialDefinitionId as string ) - setShowGetFoundationCredential(showBCIDSelector(credentialDefinitionIDs, canUseLSBCredential)) - }, [credentials, canUseLSBCredential]) + setShowGetFoundationCredential(showBCIDSelector(credentialDefinitionIDs, true)) + }, [credentials]) useEffect(() => { const handle = DeviceEventEmitter.addListener(BCWalletEventTypes.ADD_CREDENTIAL_PRESSED, (value?: boolean) => { @@ -125,34 +99,27 @@ const AddCredentialSlider: React.FC = () => { }, []) return ( - - - - - - - - - Choose - {showGetFoundationCredential && ( - - {workflowInFlight ? ( - - ) : ( - - )} - - Get your Person credential - - )} - - - Scan a QR code + + + + + + + + Choose + {showGetFoundationCredential && ( + + + Get your Person credential - + )} + + + Scan a QR code + - - + + ) } diff --git a/app/src/helpers/BCIDHelper.ts b/app/src/helpers/BCIDHelper.ts index f871fd53..58792104 100644 --- a/app/src/helpers/BCIDHelper.ts +++ b/app/src/helpers/BCIDHelper.ts @@ -168,7 +168,7 @@ export const cleanupAfterServiceCardAuthentication = (status: AuthenticationResu export const authenticateWithServiceCard = async ( store: BCState, - setWorkflow: React.Dispatch>, + setWorkflowInProgress: React.Dispatch>, agentDetails: WellKnownAgentDetails, t: TFunction<'translation', undefined>, callback?: (connectionId?: string) => void @@ -195,7 +195,7 @@ export const authenticateWithServiceCard = async ( result.type === AuthenticationResultType.Cancel && typeof (result as unknown as RedirectResult).url === 'undefined' ) { - setWorkflow(false) + setWorkflowInProgress(false) return } @@ -203,7 +203,6 @@ export const authenticateWithServiceCard = async ( result.type === AuthenticationResultType.Dismiss && typeof (result as unknown as RedirectResult).url === 'undefined' ) { - setWorkflow(false) callback && callback(agentDetails.connectionId) } @@ -214,7 +213,6 @@ export const authenticateWithServiceCard = async ( (result as unknown as RedirectResult).url.includes(did) && (result as unknown as RedirectResult).url.includes('success') ) { - setWorkflow(false) callback && callback(agentDetails.connectionId) } @@ -226,15 +224,8 @@ export const authenticateWithServiceCard = async ( (result as unknown as RedirectResult).url.includes(did) && (result as unknown as RedirectResult).url.includes('cancel') ) { - setWorkflow(false) - - // FIXME: This does nothing unlit the catch below is updated. - // throw new BifoldError( - // t('Error.Title2025'), - // t('Error.Message2025'), - // t('Error.NoMessage'), - // ErrorCodes.ServiceCardError - // ) + setWorkflowInProgress(false) + return } } else { await Linking.openURL(url) @@ -243,11 +234,9 @@ export const authenticateWithServiceCard = async ( cleanupAfterServiceCardAuthentication(AuthenticationResultType.Success) } catch (error: unknown) { const code = (error as BifoldError).code - cleanupAfterServiceCardAuthentication( code === ErrorCodes.CanceledByUser ? AuthenticationResultType.Cancel : AuthenticationResultType.Fail ) - DeviceEventEmitter.emit(BifoldEventTypes.ERROR_ADDED, error) } } @@ -255,7 +244,7 @@ export const authenticateWithServiceCard = async ( export const startFlow = async ( agent: Agent, store: BCState, - setWorkflowInFlight: React.Dispatch>, + setWorkflowInProgress: React.Dispatch>, t: TFunction<'translation', undefined>, callback?: (connectionId?: string) => void ) => { @@ -264,12 +253,11 @@ export const startFlow = async ( if (agentDetails.legacyConnectionDid !== undefined) { setTimeout(async () => { - await authenticateWithServiceCard(store, setWorkflowInFlight, agentDetails, t, callback) + await authenticateWithServiceCard(store, setWorkflowInProgress, agentDetails, t, callback) }, connectionDelayInMs) } } catch (error: unknown) { - setWorkflowInFlight(false) - + setWorkflowInProgress(false) DeviceEventEmitter.emit(BifoldEventTypes.ERROR_ADDED, error) } } diff --git a/app/src/hooks/credential-offer-trigger.ts b/app/src/hooks/credential-offer-trigger.ts new file mode 100644 index 00000000..9928756d --- /dev/null +++ b/app/src/hooks/credential-offer-trigger.ts @@ -0,0 +1,26 @@ +import { CredentialState } from '@aries-framework/core' +import { useCredentialByState } from '@aries-framework/react-hooks' +import { useNavigation } from '@react-navigation/core' +import { Screens, Stacks } from 'aries-bifold' +import { useEffect } from 'react' + +export const useCredentialOfferTrigger = (workflowConnectionId?: string): void => { + const navigation = useNavigation() + + const offers = useCredentialByState(CredentialState.OfferReceived) + + const goToCredentialOffer = (credentialId?: string) => { + navigation.getParent()?.navigate(Stacks.NotificationStack, { + screen: Screens.CredentialOffer, + params: { credentialId }, + }) + } + + useEffect(() => { + for (const credential of offers) { + if (credential.state == CredentialState.OfferReceived && credential.connectionId === workflowConnectionId) { + goToCredentialOffer(credential.id) + } + } + }, [offers, workflowConnectionId]) +} diff --git a/app/src/localization/en/index.ts b/app/src/localization/en/index.ts index b3a58b17..35ecbc1e 100644 --- a/app/src/localization/en/index.ts +++ b/app/src/localization/en/index.ts @@ -50,8 +50,8 @@ const translation = { "PersonCredential": { "Issuer": "Service BC", "Name": "Person", - "GivenName":"Sample Given Name", - "FamilyName":"Sample Family Name", + "GivenName": "Sample Given Name", + "FamilyName": "Sample Family Name", "Description": "Add your Person credential to your wallet to prove your personal information online and get access to services online.\n\nYou'll need the BC Service Card app set up on this mobile device.", "LinkDescription": "Get the BC Services Card app", "GetCredential": "Get your Person credential", diff --git a/app/src/localization/fr/index.ts b/app/src/localization/fr/index.ts index c11f05cd..231ff3bb 100644 --- a/app/src/localization/fr/index.ts +++ b/app/src/localization/fr/index.ts @@ -34,8 +34,8 @@ const translation = { "Warning": "Ensure only you have access to your wallet. (FR)", "UseToUnlock": "Use biometrics to unlock wallet? (FR)", }, - "Credentials":{ - "AddCredential":"Add Credential (FR)", + "Credentials": { + "AddCredential": "Add Credential (FR)", "EmptyList": "Your wallet is empty. (FR)", "AddFirstCredential": "Add your first credential (FR)" }, @@ -43,14 +43,14 @@ const translation = { "Onboarding": "BC Wallet (FR)" }, "PersonCredentialNotification": { - "Title":"Get your Person credential (FR)", - "Description":"Add your Person credential to your wallet and use it to get access to services online. (FR)" + "Title": "Get your Person credential (FR)", + "Description": "Add your Person credential to your wallet and use it to get access to services online. (FR)" }, "PersonCredential": { "Issuer": "Service BC (FR)", "Name": "Person (FR)", - "GivenName":"Sample Given Name (FR)", - "FamilyName":"Sample Family Name (FR)", + "GivenName": "Sample Given Name (FR)", + "FamilyName": "Sample Family Name (FR)", "Description": "Add your Person credential to your wallet to prove your personal information online and get access to services online.\n\nYou'll need the BC Service Card app set up on this mobile device. (FR)", "LinkDescription": "Get the BC Services Card app (FR)", "GetCredential": "Get your Person credential (FR)", diff --git a/app/src/localization/pt-br/index.ts b/app/src/localization/pt-br/index.ts index d8441161..c3219914 100644 --- a/app/src/localization/pt-br/index.ts +++ b/app/src/localization/pt-br/index.ts @@ -49,8 +49,8 @@ const translation = { "PersonCredential": { "Issuer": "Service BC (PT-BR)", "Name": "Person (PT-BR)", - "GivenName":"Sample Given Name (PT-BR)", - "FamilyName":"Sample Family Name (PT-BR)", + "GivenName": "Sample Given Name (PT-BR)", + "FamilyName": "Sample Family Name (PT-BR)", "Description": "Add your Person credential to your wallet to prove your personal information online and get access to services online.\n\nYou'll need the BC Service Card app set up on this mobile device. (PT-BR)", "LinkDescription": "Get the BC Services Card app (PT-BR)", "GetCredential": "Get your Person credential (PT-BR)", diff --git a/app/src/screens/PersonCredential.tsx b/app/src/screens/PersonCredential.tsx index b9bfa0a0..2ba53a54 100644 --- a/app/src/screens/PersonCredential.tsx +++ b/app/src/screens/PersonCredential.tsx @@ -1,6 +1,6 @@ import { useAgent } from '@aries-framework/react-hooks' import { useNavigation } from '@react-navigation/core' -import { Button, ButtonType, Screens, useStore, useTheme, CredentialCard } from 'aries-bifold' +import { Button, ButtonType, Screens, useStore, useTheme, CredentialCard, TabStacks } from 'aries-bifold' import React, { useState, useCallback } from 'react' import { useTranslation } from 'react-i18next' import { StyleSheet, Text, View, TouchableOpacity, Linking, FlatList } from 'react-native' @@ -8,17 +8,22 @@ import { SafeAreaView } from 'react-native-safe-area-context' import LoadingIcon from '../components/LoadingIcon' import { startFlow } from '../helpers/BCIDHelper' +import { useCredentialOfferTrigger } from '../hooks/credential-offer-trigger' import { BCDispatchAction, BCState } from '../store' const PersonCredential: React.FC = () => { - const [workflowInFlight, setWorkflowInFlight] = useState(false) const { agent } = useAgent() const navigation = useNavigation() + const [store, dispatch] = useStore() + const [workflowInProgress, setWorkflowInProgress] = useState(false) + const [workflowConnectionId, setWorkflowConnectionId] = useState() const { ColorPallet, TextTheme } = useTheme() const { t } = useTranslation() + useCredentialOfferTrigger(workflowConnectionId) + const styles = StyleSheet.create({ pageContainer: { flex: 1, @@ -41,13 +46,12 @@ const PersonCredential: React.FC = () => { type: BCDispatchAction.PERSON_CREDENTIAL_OFFER_DISMISSED, payload: [{ personCredentialOfferDismissed: true }], }) - - navigation.navigate(Screens.Home as never) + navigation.getParent()?.navigate(TabStacks.HomeStack, { screen: Screens.Home }) }, []) - const startGetBCIDCredentialWorkflow = useCallback(() => { - setWorkflowInFlight(true) - startFlow(agent!, store, setWorkflowInFlight, t, dismissPersonCredentialOffer) + const acceptPersonCredentialOffer = useCallback(() => { + setWorkflowInProgress(true) + startFlow(agent!, store, setWorkflowInProgress, t, (connectionId) => setWorkflowConnectionId(connectionId)) }, []) const getBCServicesCardApp = useCallback(() => { @@ -73,12 +77,12 @@ const PersonCredential: React.FC = () => { @@ -87,6 +91,7 @@ const PersonCredential: React.FC = () => { title={t('PersonCredential.Decline')} accessibilityLabel={t('PersonCredential.Decline')} onPress={dismissPersonCredentialOffer} + disabled={workflowInProgress} buttonType={ButtonType.Secondary} >