Skip to content

Commit

Permalink
feat(plugin): passing context to VC components (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
simonas-notcat authored Oct 11, 2023
1 parent c14c757 commit f7bfc09
Show file tree
Hide file tree
Showing 6 changed files with 695 additions and 157 deletions.
7 changes: 4 additions & 3 deletions packages/plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@
"antd": "~5.8.4",
"date-fns": "^2.30.0",
"did-jwt-vc": "^3.2.10",
"highlight.js": "^11.8.0",
"markdown-it": "^13.0.2",
"md5": "^2.3.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^9.0.0",
"react-query": "^3.39.3",
"react-router-dom": "^6.11.2",
"remark-gfm": "^4.0.0",
"typescript": "5.2.2",
"url-parse": "^1.5.10",
"uuid": "^9.0.1"
Expand All @@ -54,6 +54,7 @@
"@veramo/core": "5.5.3-next.17",
"@veramo/core-types": "5.5.3-next.17",
"@veramo/data-store": "5.5.3-next.17",
"@veramo/utils": "5.5.3-next.17"
"@veramo/utils": "5.5.3-next.17",
"unified": "^11.0.3"
}
}
39 changes: 1 addition & 38 deletions packages/plugin/src/components/CredentialActionsDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import { Dropdown, App } from 'antd'
import React from 'react'
import { useVeramo } from '@veramo-community/veramo-react'
import { useNavigate } from 'react-router-dom'
import { PicLeftOutlined } from '@ant-design/icons'
import { IDataStore, UniqueVerifiableCredential } from '@veramo/core'
import { getIssuerDID } from '../utils/did.js'
import { usePlugins } from '../PluginProvider.js'
import { MenuProps } from 'antd'

Expand All @@ -27,47 +25,12 @@ export const CredentialActionsDropdown: React.FC<{
}
})

const handleCopyEmbed = () => {
let embed = ''
if (verifiableCredential.proof?.jwt) {
embed = `\`\`\`vc+jwt\n${verifiableCredential.proof.jwt}\n\`\`\``
} else {
embed = `\`\`\`vc+json\n${JSON.stringify(verifiableCredential, null, 2)}\n\`\`\``
}
navigator.clipboard.writeText(embed)
notification.success({
message: 'Credential embed copied to clipboard',
})
}

const handleCopyReference = () => {
const reference = `\`\`\`vc+multihash\n${getIssuerDID(verifiableCredential)}/${hash}\n\`\`\``

navigator.clipboard.writeText(reference)
notification.success({
message: 'Credential reference copied to clipboard',
})
}



return (
<Dropdown
menu={{
items: [
...menuItems,
{
key: 'embed',
label: 'Copy embed',
icon: <PicLeftOutlined />,
onClick: handleCopyEmbed,
},
{
key: 'reference',
label: 'Copy reference',
icon: <PicLeftOutlined />,
onClick: handleCopyReference,
},

],
}}
>
Expand Down
123 changes: 60 additions & 63 deletions packages/plugin/src/components/MarkDown.tsx
Original file line number Diff line number Diff line change
@@ -1,74 +1,71 @@
import React, { useEffect, useState } from 'react'
import React, { PropsWithChildren, useEffect, useState } from 'react'
import { VerifiableCredentialComponent } from "./VerifiableCredentialComponent.js";
import hljs from 'highlight.js';
import MarkdownIt from 'markdown-it';
import "highlight.js/styles/base16/solarized-dark.css";
import { IDataStore, UniqueVerifiableCredential } from '@veramo/core-types';
import { useVeramo } from '@veramo-community/veramo-react';
import { Spin } from 'antd';
import { Button, Spin } from 'antd';
import { usePlugins } from '../PluginProvider.js';
import { v4 } from 'uuid'
import Markdown, { Components } from 'react-markdown'
import remarkGfm from 'remark-gfm'
import { PluggableList } from 'unified'

const md = new MarkdownIt({
html: true,
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, { language: lang }).value;
} catch (e) {
console.error(e);
/* empty */
}
}

return ''; // use external default escaping
},
})
export const MarkDown = (
{ content, credential, context }:
{ content: string, credential?: UniqueVerifiableCredential, context?: any}
) => {

export const MarkDown: React.FC<{ content: string}> = ({ content }: { content: string}) => {
const { plugins } = usePlugins()
const [ showAll, setShowAll ] = useState<boolean>(context?.textRange ? false : true)

const markDownPlugins = React.useMemo(() => {
const result: MarkdownIt.PluginSimple[] = []
const {remarkPlugins, components} = React.useMemo(() => {
const remarkPlugins: PluggableList = [remarkGfm]
let components: Partial<Components> = {}
plugins.forEach((plugin) => {
if (plugin.config?.enabled && plugin.getMarkdownPlugins) {
const mPlugins = plugin.getMarkdownPlugins()
result.push(...mPlugins)
if (plugin.config?.enabled && plugin.getRemarkPlugins) {
const rPlugins = plugin.getRemarkPlugins()
remarkPlugins.push(...rPlugins)
}
if (plugin.config?.enabled && plugin.getMarkdownComponents) {
const rComponents = plugin.getMarkdownComponents()
components = {...components, ...rComponents}
}
})
return result
return {remarkPlugins, components}
}, [plugins])

markDownPlugins.forEach((plugin) => {
console.log('use', plugin)
// @ts-ignore
md.use(plugin())
})
let str = content

const parsed = md.parse(content, {})
if (!showAll && context?.textRange) {
const [ start, end ] = context.textRange.split('-')
str = content.substring(start, end)
}

return (<>
{parsed.map((token, index) => {
return (
<>
<Markdown
remarkPlugins={remarkPlugins}
rehypePlugins={[[remarkCredentialPlugin, credential]]}
components={components}
>{str}</Markdown>
{!showAll && context?.textRange && <Button type='text' size='small' onClick={() => setShowAll(true)}>Show more</Button>}
{showAll && context?.textRange && <Button type='text' size='small' onClick={() => setShowAll(false)}>Show less</Button>}
</>
)
}

let Result: React.JSX.Element | undefined = undefined
plugins.forEach((plugin) => {
if (!Result && plugin.config?.enabled && plugin.getMarkdownComponent) {
const Component = plugin.getMarkdownComponent(token)
if (Component) {
Result = Component
}
function remarkCredentialPlugin(credential: UniqueVerifiableCredential) {
return (tree: any) => {
if (Array.isArray(tree.children)) {
tree.children.forEach((node: any) => {
if (node.type === 'element' && node.tagName === 'p') {
node.credential = credential
}
})

if (Result) {
return React.cloneElement(Result, { key: index })
}

return <div key={index} dangerouslySetInnerHTML={{ __html: md.renderer.render([token], {}, {}) }} />
})}</>);
}
});
}
};
};

export const CredentialLoader: React.FC<{ hash: string, did?: string}> = ({ hash, did }) => {
export const CredentialLoader = ({ hash, did, context } : { hash: string, did?: string, context?: any }) => {

const [credential, setCredential] = useState<UniqueVerifiableCredential>()
const { agent } = useVeramo<IDataStore>()
Expand All @@ -89,16 +86,16 @@ export const CredentialLoader: React.FC<{ hash: string, did?: string}> = ({ hash
} else {
// TRY IPFS or DIDComm
const senders = await agent?.didManagerFind({ provider: "did:peer"})
if (senders && senders.length > 0) {
const requestCredMessage = {
type: 'https://veramo.io/didcomm/brainshare/1.0/request-credential',
from: senders[0].did,
to: did,
id: v4(),
body: {
hash
},
return_route: 'all'
if (senders && senders.length > 0) {
const requestCredMessage = {
type: 'https://veramo.io/didcomm/brainshare/1.0/request-credential',
from: senders[0].did,
to: did,
id: v4(),
body: {
hash
},
return_route: 'all'
}
const packedMessage = await agent?.packDIDCommMessage({ message: requestCredMessage, packing: 'authcrypt' })
await agent?.sendDIDCommMessage({ packedMessage: packedMessage!, messageId: requestCredMessage.id, recipientDidUrl: did! })
Expand All @@ -111,6 +108,6 @@ export const CredentialLoader: React.FC<{ hash: string, did?: string}> = ({ hash
load()
}, [agent, hash])

return credential ? <VerifiableCredentialComponent credential={credential} /> : <Spin />
return credential ? <VerifiableCredentialComponent credential={credential} context={context} /> : <Spin />
}

Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import { EllipsisOutlined } from "@ant-design/icons";
import { IdentifierPopover } from "./IdentifierPopover.js";

export const VerifiableCredentialComponent = (
{ credential, verify = true } : {
{ credential, verify = true, context } : {
credential: UniqueVerifiableCredential,
verify?: boolean
context?: any
}
) => {
const { agent } = useVeramo<ICredentialVerifier & IDataStore>()
Expand Down Expand Up @@ -132,7 +133,7 @@ export const VerifiableCredentialComponent = (
</Row>
</div>}

{credential && <Component credential={credential} />}
{credential && <Component credential={credential} context={context}/>}
</div>
)

Expand Down
9 changes: 5 additions & 4 deletions packages/plugin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { MenuProps } from 'antd';
import { MenuDataItem } from '@ant-design/pro-components';
import { UniqueVerifiableCredential } from '@veramo/core-types'
import { IAgentPlugin } from '@veramo/core'
import Token from 'markdown-it/lib/token';
import { PluginSimple } from 'markdown-it';
import { Components } from 'react-markdown'
import { PluggableList } from 'unified'
export type IAgentExplorerPluginConfig = {
url: string;
enabled: boolean;
Expand All @@ -17,6 +17,7 @@ export type IRouteComponent = {

export type IVerifiableComponentProps = {
credential: UniqueVerifiableCredential
context?: any
}

export type IIdentifierHoverComponentProps = {
Expand All @@ -43,8 +44,8 @@ export type IAgentExplorerPlugin = {
getCredentialComponent?: (credential: UniqueVerifiableCredential) => React.FC<IVerifiableComponentProps> | undefined;
getIdentifierHoverComponent?: () => React.FC<IIdentifierHoverComponentProps>;
agentPlugins?: IAgentPlugin[];
getMarkdownComponent?: (token: Token) => React.JSX.Element | undefined;
getMarkdownPlugins?: () => PluginSimple[];
getMarkdownComponents?: () => Partial<Components> | undefined;
getRemarkPlugins?: () => PluggableList;
}

export interface IPlugin {
Expand Down
Loading

0 comments on commit f7bfc09

Please sign in to comment.