import React, { useCallback, useRef, useState } from "react"; import { Attachment, TemplateRegistry } from "../../types"; import { documentTemplates, noop } from "../../utils"; import { getLogger } from "../../logger"; import { HostActions } from "../frame/host.actions"; import { FrameActions, obfuscateField, updateHeight, updateTemplates } from "../frame/frame.actions"; import { HostConnector } from "../frame/HostConnector"; import { DomListener } from "../common/DomListener"; import { noAttachmentRenderer } from "./NoAttachmentRenderer"; import { OpenAttestationDocument, WrappedDocument, v2, v3, SignedVerifiableCredential } from "@trustvc/trustvc"; const { trace } = getLogger("FramedDocumentRenderer"); export function FramedDocumentRenderer({ templateRegistry, attachmentToComponent, }: { templateRegistry: TemplateRegistry; attachmentToComponent?: (attachment: Attachment, document: D) => React.FunctionComponent | null; }): JSX.Element; export function FramedDocumentRenderer({ templateRegistry, attachmentToComponent, }: { templateRegistry: TemplateRegistry; attachmentToComponent?: (attachment: Attachment, document: D) => React.FunctionComponent | null; }): JSX.Element; export function FramedDocumentRenderer({ templateRegistry, attachmentToComponent, }: { templateRegistry: TemplateRegistry; attachmentToComponent?: (attachment: Attachment, document: D) => React.FunctionComponent | null; }): JSX.Element; export function FramedDocumentRenderer({ templateRegistry, attachmentToComponent = noAttachmentRenderer, }: { templateRegistry: TemplateRegistry; attachmentToComponent?: (attachment: Attachment, document: D) => React.FunctionComponent | null; }): JSX.Element { const [document, setDocument] = useState(); const [useFallbackSource, setUseFallbackSource] = useState(false); const [errorType, setErrorType] = useState(); // used only to handle legacy setSelectTemplate function // dispatch function (below) is connected once through the frame and the reference to this function never change is // host and iframe. We need to use a reference to allow object mutation const documentForLegacyUsage = useRef(); const [rawDocument, setRawDocument] = useState< WrappedDocument | SignedVerifiableCredential >(); const [templateName, setTemplateName] = useState(); const toHost = useRef<(actions: FrameActions) => void>(noop); const templates = document ? documentTemplates(document, templateRegistry as TemplateRegistry, attachmentToComponent, useFallbackSource) : []; const templateConfiguration = templates.find((template) => template.id === templateName) || templates[0] || {}; const Template = templateConfiguration.template; const onConnected = useCallback((postMessage: (actions: FrameActions) => void) => { toHost.current = postMessage; }, []); // actions received by the parent hosting the component const dispatch = useCallback( (action: HostActions): any => { trace("in frame, received action", action.type); if (action.type === "RENDER_DOCUMENT") { setDocument(action.payload.document as D); documentForLegacyUsage.current = action.payload.document as D; if (action.payload.rawDocument) { setRawDocument(action.payload.rawDocument as any); } setUseFallbackSource(!!action.payload.useFallbackSource); if (action.payload.errorType) setErrorType(action.payload.errorType); const run = async (): Promise => { const templates = await documentTemplates( action.payload.document as D, templateRegistry as TemplateRegistry, attachmentToComponent, action.payload.useFallbackSource, ).map((template) => ({ id: template.id, label: template.label, type: template.type, })); toHost.current(updateTemplates(templates)); }; run(); } else if (action.type === "SELECT_TEMPLATE") { setTemplateName(action.payload); } else if (action.type === "GET_TEMPLATES") { const templates = documentTemplates( action.payload as D, templateRegistry as TemplateRegistry, attachmentToComponent, useFallbackSource, ).map((template) => ({ id: template.id, label: template.label, type: template.type, })); toHost.current(updateTemplates(templates)); // send the result to the iframe return templates; // react-native expect to get the result directly } else if (action.type === "PRINT") { window.print(); } else { throw new Error(`Action ${JSON.stringify(action)} is not handled`); } }, [templateRegistry, attachmentToComponent, useFallbackSource], ); window.openAttestation = dispatch; // expose different actions for direct injection return ( toHost.current(updateHeight(height))}> {document && Template && (