Skip to content

Commit

Permalink
fix: oob ok (#1193)
Browse files Browse the repository at this point in the history
Signed-off-by: Jason C. Leach <[email protected]>
  • Loading branch information
jleach authored Jul 11, 2024
1 parent 56d8263 commit d6da297
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 40 deletions.
104 changes: 71 additions & 33 deletions packages/legacy/core/App/screens/Connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { useNotifications } from '../hooks/notifications'
import { DeliveryStackParams, Screens, Stacks, TabStacks } from '../types/navigators'
import { testIdWithKey } from '../utils/testable'

import { useContainer, TOKENS } from './../container-api'

type ConnectionProps = StackScreenProps<DeliveryStackParams, Screens.Connection>

type MergeFunction = (current: LocalState, next: Partial<LocalState>) => LocalState
Expand All @@ -40,8 +42,10 @@ const Connection: React.FC<ConnectionProps> = ({ navigation, route }) => {
const { notifications } = useNotifications()
const { ColorPallet, TextTheme } = useTheme()
const { ConnectionLoading } = useAnimatedComponents()
const container = useContainer()
const logger = container.resolve(TOKENS.UTIL_LOGGER)
const connection = connectionId ? useConnectionById(connectionId) : undefined
const oobRecord = useOutOfBandByConnectionId(connectionId ?? '')
const oobRecord = connectionId ? useOutOfBandByConnectionId(connectionId) : undefined
const goalCode = oobRecord?.outOfBandInvitation.goalCode
const merge: MergeFunction = (current, next) => ({ ...current, ...next })
const [state, dispatch] = useReducer(merge, {
Expand Down Expand Up @@ -80,9 +84,14 @@ const Connection: React.FC<ConnectionProps> = ({ navigation, route }) => {

const goalCodeAction = (goalCode: string): (() => void) => {
const codes: { [key: string]: undefined | (() => void) } = {
'aries.vc.verify': () => navigation.navigate(Screens.ProofRequest, { proofId: state.notificationRecord.id }),
'aries.vc.issue': () =>
navigation.navigate(Screens.CredentialOffer, { credentialId: state.notificationRecord.id }),
'aries.vc.verify': () => {
logger?.info('Connection: Handling aries.vc.verify goal code, navigate to ProofRequest')
navigation.navigate(Screens.ProofRequest, { proofId: state.notificationRecord.id })
},
'aries.vc.issue': () => {
logger?.info('Connection: Handling aries.vc.issue goal code, navigate to CredentialOffer')
navigation.navigate(Screens.CredentialOffer, { credentialId: state.notificationRecord.id })
},
}
let action = codes[goalCode]

Expand Down Expand Up @@ -140,6 +149,8 @@ const Connection: React.FC<ConnectionProps> = ({ navigation, route }) => {
useEffect(() => {
// for non-connectionless, invalid connections stay in the below state
if (connection && connection.state === DidExchangeState.RequestSent) {
logger?.info('Connection: Skipping invalid connection state')

return
}

Expand All @@ -150,6 +161,8 @@ const Connection: React.FC<ConnectionProps> = ({ navigation, route }) => {
state.notificationRecord &&
state.notificationRecord.state === 'request-received'
) {
logger?.info('Connection: Handling connectionless proof request')

navigation.replace(Screens.ProofRequest, { proofId: state.notificationRecord.id })
dispatch({ isVisible: false })

Expand All @@ -163,6 +176,8 @@ const Connection: React.FC<ConnectionProps> = ({ navigation, route }) => {
) {
// No goal code, we don't know what to expect next,
// navigate to the chat screen and set home screen as only history
logger?.info('Connection: Handling connection with OOB, without goal recognized code')

navigation.getParent()?.dispatch(
CommonActions.reset({
index: 1,
Expand All @@ -175,6 +190,8 @@ const Connection: React.FC<ConnectionProps> = ({ navigation, route }) => {
}

if (state.notificationRecord && goalCode) {
logger?.info('Connection: Handling valid goal code')

goalCodeAction(goalCode)()
}
}, [connection, connection?.state, oobRecord, goalCode, state.notificationRecord])
Expand All @@ -192,35 +209,56 @@ const Connection: React.FC<ConnectionProps> = ({ navigation, route }) => {
)

useEffect(() => {
if (state.isVisible) {
for (const notification of notifications) {
// no action taken, we're already processing a notification
if (state.notificationRecord) {
break
}

// no action taken for BasicMessageRecords
if (notification.type === 'BasicMessageRecord') {
continue
}

// Connection based, we need to match the connectionId.
if (connection && notification.connectionId === connection.id) {
dispatch({ notificationRecord: notification, isVisible: false })
break
}

// Connectionless, we need to match the threadId or parentThreadId.
if (threadId && (notification.threadId === threadId || notification.parentThreadId == threadId)) {
dispatch({ notificationRecord: notification, isVisible: false })
break
}

// OOB with `goalCode` will be checked in another `useEffect`.
if (oobRecord && goalCode) {
dispatch({ notificationRecord: notification, isVisible: false })
break
}
if (!state.isVisible) {
return
}

for (const notification of notifications) {
// no action taken, we're already processing a notification
if (state.notificationRecord) {
logger?.info('Connection: Already processing a notification')
break
}

// no action taken for BasicMessageRecords
if (notification.type === 'BasicMessageRecord') {
logger?.info('Connection: BasicMessageRecord, skipping')
continue
}

// Connection based, we need to match the connectionId.
if (
connection &&
notification.connectionId === connection.id &&
(!oobRecord || connection.outOfBandId !== oobRecord.id)
) {
logger?.info('Connection: Handling connection based')

dispatch({ notificationRecord: notification, isVisible: false })
break
}

// Connectionless, we need to match the threadId or parentThreadId.
if (threadId && (notification.threadId === threadId || notification.parentThreadId == threadId)) {
logger?.info('Connection: Handling connectionless')

dispatch({ notificationRecord: notification, isVisible: false })
break
}

// OOB with `goalCode` will be checked in another `useEffect`. Expected
// as part of a OOB.
if (
goalCode &&
oobRecord &&
connection &&
connection.outOfBandId === oobRecord.id &&
connection.id === notification.connectionId
) {
logger?.info('Connection: Handling OOB with goalCode')

dispatch({ notificationRecord: notification, isVisible: false })
break
}
}
}, [notifications])
Expand Down
18 changes: 12 additions & 6 deletions packages/legacy/core/__tests__/screens/Connection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import timeTravel from '../helpers/timetravel'

const proofNotifPath = path.join(__dirname, '../fixtures/proof-notif.json')
const proofNotif = JSON.parse(fs.readFileSync(proofNotifPath, 'utf8'))
const offerNotifPath = path.join(__dirname, '../fixtures/offer-notif.json')
const offerNotif = JSON.parse(fs.readFileSync(offerNotifPath, 'utf8'))
const connectionPath = path.join(__dirname, '../fixtures/connection-v1.json')
const connection = JSON.parse(fs.readFileSync(connectionPath, 'utf8'))
const connectionResponseReceivedPath = path.join(__dirname, '../fixtures/connection-v1-response-received.json')
Expand All @@ -28,6 +30,7 @@ const props = { params: { connectionId: connection.id } }

jest.useFakeTimers({ legacyFakeTimers: true })
jest.spyOn(global, 'setTimeout')
jest.mock('../../App/container-api')
jest.mock('@react-navigation/core', () => {
return require('../../__mocks__/custom/@react-navigation/core')
})
Expand Down Expand Up @@ -211,17 +214,18 @@ describe('ConnectionModal Component', () => {
expect(navigation.replace).toBeCalledWith('Proof Request', { proofId: proofNotif.id })
})

test('Goal code extracted and navigation to Chat', async () => {
test('Goal code extracted, navigation to accept offer', async () => {
const connectionId = 'abc123'
const oobId = 'def456'
const navigation = useNavigation()
// @ts-ignore-next-line
useNotifications.mockReturnValue({ total: 1, notifications: [proofNotif] })
useNotifications.mockReturnValue({ total: 1, notifications: [{ ...offerNotif, connectionId }] })
// @ts-ignore-next-line
useOutOfBandByConnectionId.mockReturnValue({ outOfBandInvitation })
useOutOfBandByConnectionId.mockReturnValue({ id: oobId, outOfBandInvitation: { goalCode: 'aries.vc.issue' } })
// @ts-ignore-next-line
useConnectionById.mockReturnValue(undefined)
useConnectionById.mockReturnValue({ ...connection, id: connectionId, outOfBandId: oobId, state: 'offer-received' })
// @ts-ignore-next-line
useProofById.mockReturnValue(proofNotif)
useProofById.mockReturnValue(offerNotif)

const element = (
<ConfigurationContext.Provider value={configurationContext}>
Expand All @@ -233,7 +237,9 @@ describe('ConnectionModal Component', () => {

expect(tree).toMatchSnapshot()
expect(navigation.navigate).toBeCalledTimes(1)
expect(navigation.navigate).toBeCalledWith('Proof Request', { proofId: proofNotif.id })
expect(navigation.navigate).toBeCalledWith('Credential Offer', {
credentialId: offerNotif.id,
})
})

test('Dismiss navigates Home', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ exports[`ConnectionModal Component Dismiss on demand 1`] = `
</Modal>
`;

exports[`ConnectionModal Component Goal code extracted and navigation to Chat 1`] = `
exports[`ConnectionModal Component Goal code extracted, navigation to accept offer 1`] = `
<Modal
animationType="slide"
hardwareAccelerated={false}
Expand Down

0 comments on commit d6da297

Please sign in to comment.