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

fix: person credential workflow pt. 2 #973

Merged
merged 15 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
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
86 changes: 34 additions & 52 deletions app/src/components/AddCredentialSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Icon from 'react-native-vector-icons/MaterialCommunityIcons'

import { BCWalletEventTypes } from '../events/eventTypes'
import { showBCIDSelector, startFlow } from '../helpers/BCIDHelper'
import { useCredentialOfferTrigger } from '../hooks/credential-offer-trigger'
import { BCState } from '../store'

import LoadingIcon from './LoadingIcon'
Expand All @@ -17,21 +18,20 @@ const AddCredentialSlider: React.FC = () => {
const { ColorPallet, TextTheme } = useTheme()
const { agent } = useAgent()
const { t } = useTranslation()
const navigation = useNavigation()

const [store] = useStore<BCState>()
const [showGetFoundationCredential, setShowGetFoundationCredential] = useState<boolean>(false)
const [addCredentialPressed, setAddCredentialPressed] = useState<boolean>(false)
const [workflowInFlight, setWorkflowInFlight] = useState<boolean>(false)
const [showGetFoundationCredential, setShowGetFoundationCredential] = useState<boolean>(false)
const [workflowInProgress, setWorkflowInProgress] = useState<boolean>(false)
const [workflowConnectionId, setWorkflowConnectionId] = useState<string | undefined>()

const offers = useCredentialByState(CredentialState.OfferReceived)

const credentials = [
...useCredentialByState(CredentialState.CredentialReceived),
...useCredentialByState(CredentialState.Done),
]

const navigation = useNavigation()
const [canUseLSBCredential] = useState<boolean>(true)
useCredentialOfferTrigger(workflowConnectionId)

const styles = StyleSheet.create({
centeredView: {
Expand Down Expand Up @@ -79,39 +79,23 @@ 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))
setWorkflowInProgress(true)
startFlow(agent!, store, setWorkflowInProgress, 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])

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) => {
Expand All @@ -125,34 +109,32 @@ const AddCredentialSlider: React.FC = () => {
}, [])

return (
<View>
<Modal animationType="slide" transparent={true} visible={addCredentialPressed} onRequestClose={deactivateSlider}>
<TouchableOpacity style={styles.outsideListener} onPress={deactivateSlider} />
<View style={styles.centeredView}>
<View style={styles.modalView}>
<TouchableOpacity onPress={deactivateSlider}>
<Icon name="window-close" size={35} style={styles.drawerRowItem}></Icon>
</TouchableOpacity>
<Text style={styles.drawerTitleText}>Choose</Text>
{showGetFoundationCredential && (
<TouchableOpacity style={styles.drawerRow} disabled={workflowInFlight} onPress={onBCIDPress}>
{workflowInFlight ? (
<LoadingIcon size={30} color={styles.drawerRowItem.color} active={workflowInFlight} />
) : (
<Icon name="credit-card" size={30} style={styles.drawerRowItem}></Icon>
)}

<Text style={{ ...styles.drawerRowItem, marginLeft: 5 }}>Get your Person credential</Text>
</TouchableOpacity>
)}
<TouchableOpacity style={styles.drawerRow} onPress={goToScanScreen}>
<Icon name="qrcode" size={30} style={styles.drawerRowItem}></Icon>
<Text style={{ ...styles.drawerRowItem, marginLeft: 5 }}>Scan a QR code</Text>
<Modal animationType="slide" transparent={true} visible={addCredentialPressed} onRequestClose={deactivateSlider}>
<TouchableOpacity style={styles.outsideListener} onPress={deactivateSlider} />
<View style={styles.centeredView}>
<View style={styles.modalView}>
<TouchableOpacity onPress={deactivateSlider}>
<Icon name="window-close" size={35} style={styles.drawerRowItem}></Icon>
</TouchableOpacity>
<Text style={styles.drawerTitleText}>Choose</Text>
{showGetFoundationCredential && (
<TouchableOpacity style={styles.drawerRow} disabled={workflowInProgress} onPress={onBCIDPress}>
{workflowInProgress ? (
<LoadingIcon size={30} color={styles.drawerRowItem.color} active={workflowInProgress} />
) : (
<Icon name="credit-card" size={30} style={styles.drawerRowItem}></Icon>
)}

<Text style={{ ...styles.drawerRowItem, marginLeft: 5 }}>Get your Person credential</Text>
</TouchableOpacity>
</View>
)}
<TouchableOpacity style={styles.drawerRow} onPress={goToScanScreen}>
<Icon name="qrcode" size={30} style={styles.drawerRowItem}></Icon>
<Text style={{ ...styles.drawerRowItem, marginLeft: 5 }}>Scan a QR code</Text>
</TouchableOpacity>
</View>
</Modal>
</View>
</View>
</Modal>
)
}

Expand Down
26 changes: 7 additions & 19 deletions app/src/helpers/BCIDHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export const cleanupAfterServiceCardAuthentication = (status: AuthenticationResu

export const authenticateWithServiceCard = async (
store: BCState,
setWorkflow: React.Dispatch<React.SetStateAction<boolean>>,
setWorkflowInProgress: React.Dispatch<React.SetStateAction<boolean>>,
agentDetails: WellKnownAgentDetails,
t: TFunction<'translation', undefined>,
callback?: (connectionId?: string) => void
Expand All @@ -195,15 +195,14 @@ export const authenticateWithServiceCard = async (
result.type === AuthenticationResultType.Cancel &&
typeof (result as unknown as RedirectResult).url === 'undefined'
) {
setWorkflow(false)
setWorkflowInProgress(false)
return
}

if (
result.type === AuthenticationResultType.Dismiss &&
typeof (result as unknown as RedirectResult).url === 'undefined'
) {
setWorkflow(false)
callback && callback(agentDetails.connectionId)
}

Expand All @@ -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)
}

Expand All @@ -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)
Expand All @@ -243,19 +234,17 @@ 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)
}
}

export const startFlow = async (
agent: Agent,
store: BCState,
setWorkflowInFlight: React.Dispatch<React.SetStateAction<boolean>>,
setWorkflowInProgress: React.Dispatch<React.SetStateAction<boolean>>,
t: TFunction<'translation', undefined>,
callback?: (connectionId?: string) => void
) => {
Expand All @@ -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)
}
}
26 changes: 26 additions & 0 deletions app/src/hooks/credential-offer-trigger.ts
Original file line number Diff line number Diff line change
@@ -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])
}
21 changes: 13 additions & 8 deletions app/src/screens/PersonCredential.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean>(false)
const { agent } = useAgent()
const navigation = useNavigation()

const [store, dispatch] = useStore<BCState>()
const [workflowInProgress, setWorkflowInProgress] = useState<boolean>(false)
const [workflowConnectionId, setWorkflowConnectionId] = useState<string | undefined>()

const { ColorPallet, TextTheme } = useTheme()
const { t } = useTranslation()

useCredentialOfferTrigger(workflowConnectionId)

const styles = StyleSheet.create({
pageContainer: {
flex: 1,
Expand All @@ -45,9 +50,9 @@ const PersonCredential: React.FC = () => {
navigation.navigate(Screens.Home as never)
}, [])

const startGetBCIDCredentialWorkflow = useCallback(() => {
setWorkflowInFlight(true)
startFlow(agent!, store, setWorkflowInFlight, t, dismissPersonCredentialOffer)
const onBCIDPress = useCallback(() => {
setWorkflowInProgress(true)
startFlow(agent!, store, setWorkflowInProgress, t, (connectionId) => setWorkflowConnectionId(connectionId))
}, [])

const getBCServicesCardApp = useCallback(() => {
Expand All @@ -73,12 +78,12 @@ const PersonCredential: React.FC = () => {
<Button
title={t('PersonCredential.GetCredential')}
accessibilityLabel={t('PersonCredential.GetCredential')}
onPress={startGetBCIDCredentialWorkflow}
disabled={workflowInFlight}
onPress={onBCIDPress}
disabled={workflowInProgress}
buttonType={ButtonType.Primary}
>
{workflowInFlight && (
<LoadingIcon color={ColorPallet.grayscale.white} size={35} active={workflowInFlight} />
{workflowInProgress && (
<LoadingIcon color={ColorPallet.grayscale.white} size={35} active={workflowInProgress} />
)}
</Button>
</View>
Expand Down