Skip to content

Commit

Permalink
feat(hooks): added proof and credential format data hooks (#231)
Browse files Browse the repository at this point in the history
Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
berendsliedrecht authored Dec 11, 2023
1 parent 31c6e63 commit d03be3b
Show file tree
Hide file tree
Showing 11 changed files with 422 additions and 118 deletions.
9 changes: 4 additions & 5 deletions packages/react-hooks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,16 @@
"test": "jest"
},
"dependencies": {
"@aries-framework/question-answer": "^0.4.0",
"@types/node-fetch": "^2",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@aries-framework/core": "0.4.0",
"react": "^18.0.0",
"@aries-framework/core": "^0.4.2",
"@aries-framework/question-answer": "^0.4.2",
"@types/react": "^18.2.14"
},
"peerDependencies": {
"@aries-framework/core": "^0.4.0",
"@aries-framework/question-answer": "^0.4.2",
"@aries-framework/core": "^0.4.2",
"react": ">=17.0.0 <19.0.0"
}
}
16 changes: 13 additions & 3 deletions packages/react-hooks/src/AgentProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import { createContext, useState, useContext } from 'react'

import BasicMessageProvider from './BasicMessageProvider'
import ConnectionProvider from './ConnectionProvider'
import CredentialFormatDataProvider from './CredentialFormatDataProvider'
import CredentialProvider from './CredentialProvider'
import ProofFormatDataProvider from './ProofFormatDataProvider'
import ProofProvider from './ProofProvider'
import QuestionAnswerProvider from './QuestionAnswerProvider'
import { useIsModuleRegistered } from './recordUtils'
Expand Down Expand Up @@ -43,9 +45,17 @@ const AgentProvider: React.FC<PropsWithChildren<Props>> = ({ agent, children })
<ConnectionProvider agent={agent}>
<CredentialProvider agent={agent}>
<ProofProvider agent={agent}>
<BasicMessageProvider agent={agent}>
{isQaRegistered ? <QuestionAnswerProvider agent={agent}>{children} </QuestionAnswerProvider> : children}
</BasicMessageProvider>
<CredentialFormatDataProvider agent={agent}>
<ProofFormatDataProvider agent={agent}>
<BasicMessageProvider agent={agent}>
{isQaRegistered ? (
<QuestionAnswerProvider agent={agent}>{children} </QuestionAnswerProvider>
) : (
children
)}
</BasicMessageProvider>
</ProofFormatDataProvider>
</CredentialFormatDataProvider>
</ProofProvider>
</CredentialProvider>
</ConnectionProvider>
Expand Down
42 changes: 20 additions & 22 deletions packages/react-hooks/src/BasicMessageProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,35 +47,33 @@ const BasicMessageProvider: React.FC<PropsWithChildren<Props>> = ({ agent, child
})

const setInitialState = async () => {
if (agent) {
const records = await agent.basicMessages.findAllByQuery({})
setState({ records, loading: false })
}
const records = await agent.basicMessages.findAllByQuery({})
setState({ records, loading: false })
}

useEffect(() => {
setInitialState()
}, [agent])

useEffect(() => {
if (!state.loading) {
const basicMessageAdded$ = recordsAddedByType(agent, BasicMessageRecord).subscribe((record) =>
setState(addRecord(record, state))
)

const basicMessageUpdated$ = recordsUpdatedByType(agent, BasicMessageRecord).subscribe((record) =>
setState(updateRecord(record, state))
)

const basicMessageRemoved$ = recordsRemovedByType(agent, BasicMessageRecord).subscribe((record) =>
setState(removeRecord(record, state))
)

return () => {
basicMessageAdded$?.unsubscribe()
basicMessageUpdated$?.unsubscribe()
basicMessageRemoved$?.unsubscribe()
}
if (state.loading) return

const basicMessageAdded$ = recordsAddedByType(agent, BasicMessageRecord).subscribe((record) =>
setState(addRecord(record, state))
)

const basicMessageUpdated$ = recordsUpdatedByType(agent, BasicMessageRecord).subscribe((record) =>
setState(updateRecord(record, state))
)

const basicMessageRemoved$ = recordsRemovedByType(agent, BasicMessageRecord).subscribe((record) =>
setState(removeRecord(record, state))
)

return () => {
basicMessageAdded$?.unsubscribe()
basicMessageUpdated$?.unsubscribe()
basicMessageRemoved$?.unsubscribe()
}
}, [state, agent])

Expand Down
42 changes: 20 additions & 22 deletions packages/react-hooks/src/ConnectionProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,35 +72,33 @@ const ConnectionProvider: React.FC<PropsWithChildren<Props>> = ({ agent, childre
})

const setInitialState = async () => {
if (agent) {
const records = await agent.connections.getAll()
setState({ records, loading: false })
}
const records = await agent.connections.getAll()
setState({ records, loading: false })
}

useEffect(() => {
setInitialState()
}, [agent])

useEffect(() => {
if (!state.loading) {
const connectionAdded$ = recordsAddedByType(agent, ConnectionRecord).subscribe((record) =>
setState(addRecord(record, state))
)

const connectionUpdated$ = recordsUpdatedByType(agent, ConnectionRecord).subscribe((record) =>
setState(updateRecord(record, state))
)

const connectionRemoved$ = recordsRemovedByType(agent, ConnectionRecord).subscribe((record) =>
setState(removeRecord(record, state))
)

return () => {
connectionAdded$.unsubscribe()
connectionUpdated$.unsubscribe()
connectionRemoved$.unsubscribe()
}
if (state.loading) return

const connectionAdded$ = recordsAddedByType(agent, ConnectionRecord).subscribe((record) =>
setState(addRecord(record, state))
)

const connectionUpdated$ = recordsUpdatedByType(agent, ConnectionRecord).subscribe((record) =>
setState(updateRecord(record, state))
)

const connectionRemoved$ = recordsRemovedByType(agent, ConnectionRecord).subscribe((record) =>
setState(removeRecord(record, state))
)

return () => {
connectionAdded$.unsubscribe()
connectionUpdated$.unsubscribe()
connectionRemoved$.unsubscribe()
}
}, [state, agent])

Expand Down
134 changes: 134 additions & 0 deletions packages/react-hooks/src/CredentialFormatDataProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import type { Agent } from '@aries-framework/core'
import type { Awaited } from '@aries-framework/core/build/types'
import type { PropsWithChildren } from 'react'

import { CredentialExchangeRecord } from '@aries-framework/core'
import React, { useState, createContext, useContext, useEffect } from 'react'

import { recordsAddedByType, recordsRemovedByType, recordsUpdatedByType } from './recordUtils'

type FormatReturn = Awaited<ReturnType<Agent['credentials']['getFormatData']>>

export type CredentialFormatData = FormatReturn & {
id: string
}

type FormattedDataState = {
formattedData: Array<CredentialFormatData>
loading: boolean
}

const addRecord = (record: CredentialFormatData, state: FormattedDataState): FormattedDataState => {
const newRecordsState = [...state.formattedData]
newRecordsState.unshift(record)

return {
loading: state.loading,
formattedData: newRecordsState,
}
}

const updateRecord = (record: CredentialFormatData, state: FormattedDataState): FormattedDataState => {
const newRecordsState = [...state.formattedData]
const index = newRecordsState.findIndex((r) => r.id === record.id)

if (index > -1) {
newRecordsState[index] = record
}

return {
loading: state.loading,
formattedData: newRecordsState,
}
}

const removeRecord = (recordId: string, state: FormattedDataState): FormattedDataState => {
const newRecordsState = state.formattedData.filter((r) => r.id !== recordId)

return {
loading: state.loading,
formattedData: newRecordsState,
}
}

const CredentialFormatDataContext = createContext<FormattedDataState | undefined>(undefined)

export const useCredentialsFormatData = () => {
const credentialFormatDataContext = useContext(CredentialFormatDataContext)

if (!credentialFormatDataContext) {
throw new Error('useCredentialFormatData must be used within a CredentialFormatDataContextProvider')
}

return credentialFormatDataContext
}

export const useCredentialFormatDataById = (id: string): CredentialFormatData | undefined => {
const { formattedData } = useCredentialsFormatData()
return formattedData.find((c) => c.id === id)
}

interface Props {
agent: Agent
}

const CredentialFormatDataProvider: React.FC<PropsWithChildren<Props>> = ({ agent, children }) => {
const [state, setState] = useState<{
formattedData: Array<CredentialFormatData>
loading: boolean
}>({
formattedData: [],
loading: true,
})

const fetchCredentialInformation = async (agent: Agent, record: CredentialExchangeRecord) => {
const formatData = await agent.credentials.getFormatData(record.id)

return { ...formatData, id: record.id }
}

const setInitialState = async () => {
const records = await agent.credentials.getAll()
const formattedData: Array<CredentialFormatData> = []
for (const record of records) {
formattedData.push(await fetchCredentialInformation(agent, record))
}
setState({ formattedData, loading: false })
}

useEffect(() => {
void setInitialState()
}, [agent])

useEffect(() => {
if (state.loading) return

const credentialAdded$ = recordsAddedByType(agent, CredentialExchangeRecord).subscribe(
async (record: CredentialExchangeRecord) => {
const formatData = await fetchCredentialInformation(agent, record)
setState(addRecord(formatData, state))
}
)

const credentialUpdate$ = recordsUpdatedByType(agent, CredentialExchangeRecord).subscribe(
async (record: CredentialExchangeRecord) => {
const formatData = await fetchCredentialInformation(agent, record)
setState(updateRecord(formatData, state))
}
)

const credentialRemove$ = recordsRemovedByType(agent, CredentialExchangeRecord).subscribe((record) =>
setState(removeRecord(record.id, state))
)

return () => {
credentialAdded$.unsubscribe()
credentialUpdate$.unsubscribe()
credentialRemove$.unsubscribe()
}
}, [state, agent])

return <CredentialFormatDataContext.Provider value={state}>{children}</CredentialFormatDataContext.Provider>
}

export default CredentialFormatDataProvider
53 changes: 23 additions & 30 deletions packages/react-hooks/src/CredentialProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ export const useCredentialByState = (state: CredentialState | CredentialState[])
const { records: credentials } = useCredentials()

const filteredCredentials = useMemo(
() =>
credentials.filter((r: CredentialExchangeRecord) => {
if (states.includes(r.state)) return r
}),
() => credentials.filter((r: CredentialExchangeRecord) => states.includes(r.state)),
[credentials]
)
return filteredCredentials
Expand All @@ -59,12 +56,10 @@ export const useCredentialNotInState = (state: CredentialState | CredentialState
const { records: credentials } = useCredentials()

const filteredCredentials = useMemo(
() =>
credentials.filter((r: CredentialExchangeRecord) => {
if (!states.includes(r.state)) return r
}),
() => credentials.filter((r: CredentialExchangeRecord) => !states.includes(r.state)),
[credentials]
)

return filteredCredentials
}

Expand All @@ -79,35 +74,33 @@ const CredentialProvider: React.FC<PropsWithChildren<Props>> = ({ agent, childre
})

const setInitialState = async () => {
if (agent) {
const records = await agent.credentials.getAll()
setState({ records, loading: false })
}
const records = await agent.credentials.getAll()
setState({ records, loading: false })
}

useEffect(() => {
setInitialState()
}, [agent])

useEffect(() => {
if (!state.loading) {
const credentialAdded$ = recordsAddedByType(agent, CredentialExchangeRecord).subscribe((record) =>
setState(addRecord(record, state))
)

const credentialUpdated$ = recordsUpdatedByType(agent, CredentialExchangeRecord).subscribe((record) =>
setState(updateRecord(record, state))
)

const credentialRemoved$ = recordsRemovedByType(agent, CredentialExchangeRecord).subscribe((record) =>
setState(removeRecord(record, state))
)

return () => {
credentialAdded$?.unsubscribe()
credentialUpdated$?.unsubscribe()
credentialRemoved$?.unsubscribe()
}
if (state.loading) return

const credentialAdded$ = recordsAddedByType(agent, CredentialExchangeRecord).subscribe((record) =>
setState(addRecord(record, state))
)

const credentialUpdated$ = recordsUpdatedByType(agent, CredentialExchangeRecord).subscribe((record) =>
setState(updateRecord(record, state))
)

const credentialRemoved$ = recordsRemovedByType(agent, CredentialExchangeRecord).subscribe((record) =>
setState(removeRecord(record, state))
)

return () => {
credentialAdded$?.unsubscribe()
credentialUpdated$?.unsubscribe()
credentialRemoved$?.unsubscribe()
}
}, [state, agent])

Expand Down
Loading

0 comments on commit d03be3b

Please sign in to comment.