import { CreateSignatureEventContent, Meta } from '../types' import { fromUnixTimestamp, parseJson } from '.' import { Event, verifyEvent } from 'nostr-tools' import { toast } from 'react-toastify' import { extractFileExtensions } from './file' import { handleError } from '../types/errors' import { MetaParseError } from '../types/errors/MetaParseError' export enum SignStatus { Signed = 'Signed', Awaiting = 'Awaiting', Pending = 'Pending', Invalid = 'Invalid', Viewer = 'Viewer' } export enum SigitStatus { Partial = 'In-Progress', Complete = 'Completed' } // Reuse common error messages for meta parsing export enum SigitMetaParseErrorType { 'PARSE_ERROR_EVENT' = 'error occurred in parsing the create signature event', 'PARSE_ERROR_SIGNATURE_EVENT_CONTENT' = "err in parsing the createSignature event's content" } export interface SigitCardDisplayInfo { createdAt?: number title?: string submittedBy?: `npub1${string}` signers: `npub1${string}`[] fileExtensions: string[] signedStatus: SigitStatus isValid: boolean } /** * Wrapper for event parser that throws custom SigitMetaParseError with cause and context * @param raw Raw string for parsing * @returns parsed Event */ export const parseNostrEvent = async (raw: string): Promise => { try { const event = await parseJson(raw) return event } catch (error) { throw new MetaParseError(SigitMetaParseErrorType.PARSE_ERROR_EVENT, { cause: handleError(error), context: raw }) } } /** * Wrapper for event content parser that throws custom SigitMetaParseError with cause and context * @param raw Raw string for parsing * @returns parsed CreateSignatureEventContent */ export const parseCreateSignatureEventContent = async ( raw: string ): Promise => { try { const createSignatureEventContent = await parseJson(raw) return createSignatureEventContent } catch (error) { throw new MetaParseError( SigitMetaParseErrorType.PARSE_ERROR_SIGNATURE_EVENT_CONTENT, { cause: handleError(error), context: raw } ) } } /** * Extracts only necessary metadata for the card display * @param meta Sigit metadata * @returns SigitCardDisplayInfo */ export const extractSigitCardDisplayInfo = async (meta: Meta) => { if (!meta?.createSignature) return const sigitInfo: SigitCardDisplayInfo = { signers: [], fileExtensions: [], signedStatus: SigitStatus.Partial, isValid: false } try { const createSignatureEvent = await parseNostrEvent(meta.createSignature) sigitInfo.isValid = verifyEvent(createSignatureEvent) // created_at in nostr events are stored in seconds sigitInfo.createdAt = fromUnixTimestamp(createSignatureEvent.created_at) const createSignatureContent = await parseCreateSignatureEventContent( createSignatureEvent.content ) const files = Object.keys(createSignatureContent.fileHashes) const { extensions } = extractFileExtensions(files) const signedBy = Object.keys(meta.docSignatures) as `npub1${string}`[] const isCompletelySigned = createSignatureContent.signers.every((signer) => signedBy.includes(signer) ) sigitInfo.title = createSignatureContent.title sigitInfo.submittedBy = createSignatureEvent.pubkey as `npub1${string}` sigitInfo.signers = createSignatureContent.signers sigitInfo.fileExtensions = extensions if (isCompletelySigned) { sigitInfo.signedStatus = SigitStatus.Complete } return sigitInfo } catch (error) { if (error instanceof MetaParseError) { toast.error(error.message) console.error(error.name, error.message, error.cause, error.context) } else { console.error('Unexpected error', error) } } }