Compare commits
11 Commits
35b1378aa7
...
20d1170f7d
Author | SHA1 | Date | |
---|---|---|---|
20d1170f7d | |||
a8020e6db2 | |||
1c998ab99f | |||
fa1811a330 | |||
624afae851 | |||
3a03e3c7e8 | |||
5570d72f79 | |||
9dc160e89c | |||
ec305c417b | |||
722aecda39 | |||
f4aefbb200 |
@ -23,7 +23,7 @@ interface PdfMarkingProps {
|
||||
meta: Meta | null
|
||||
otherUserMarks: Mark[]
|
||||
setCurrentUserMarks: (currentUserMarks: CurrentUserMark[]) => void
|
||||
setIsReadyToSign: (isReadyToSign: boolean) => void
|
||||
setIsMarksCompleted: (isMarksCompleted: boolean) => void
|
||||
setUpdatedMarks: (markToUpdate: Mark) => void
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ const PdfMarking = (props: PdfMarkingProps) => {
|
||||
const {
|
||||
files,
|
||||
currentUserMarks,
|
||||
setIsReadyToSign,
|
||||
setIsMarksCompleted,
|
||||
setCurrentUserMarks,
|
||||
setUpdatedMarks,
|
||||
handleDownload,
|
||||
@ -101,7 +101,7 @@ const PdfMarking = (props: PdfMarkingProps) => {
|
||||
)
|
||||
setCurrentUserMarks(updatedCurrentUserMarks)
|
||||
setSelectedMark(null)
|
||||
setIsReadyToSign(true)
|
||||
setIsMarksCompleted(true)
|
||||
setUpdatedMarks(updatedMark.mark)
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ import {
|
||||
hexToNpub,
|
||||
parseNostrEvent,
|
||||
parseCreateSignatureEventContent,
|
||||
SigitMetaParseError,
|
||||
SigitStatus,
|
||||
SignStatus
|
||||
} from '../utils'
|
||||
@ -21,6 +20,7 @@ import { Event } from 'nostr-tools'
|
||||
import store from '../store/store'
|
||||
import { AuthState } from '../store/auth/types'
|
||||
import { NostrController } from '../controllers'
|
||||
import { MetaParseError } from '../types/errors/MetaParseError'
|
||||
|
||||
/**
|
||||
* Flattened interface that combines properties `Meta`, `CreateSignatureEventContent`,
|
||||
@ -247,7 +247,7 @@ export const useSigitMeta = (meta: Meta): FlatMeta => {
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof SigitMetaParseError) {
|
||||
if (error instanceof MetaParseError) {
|
||||
toast.error(error.message)
|
||||
}
|
||||
console.error(error)
|
||||
|
@ -17,8 +17,10 @@ export const StickySideColumns = ({
|
||||
<div className={`${styles.sidesWrap} ${styles.files}`}>
|
||||
<div className={styles.sides}>{left}</div>
|
||||
</div>
|
||||
<div id="content-preview" className={styles.content}>
|
||||
{children}
|
||||
<div>
|
||||
<div id="content-preview" className={styles.content}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.sidesWrap}>
|
||||
<div className={styles.sides}>{right}</div>
|
||||
|
@ -514,6 +514,9 @@ export const CreatePage = () => {
|
||||
return (
|
||||
file.pages?.flatMap((page, index) => {
|
||||
return page.drawnFields.map((drawnField) => {
|
||||
if (!drawnField.counterpart) {
|
||||
throw new Error('Missing counterpart')
|
||||
}
|
||||
return {
|
||||
type: drawnField.type,
|
||||
location: {
|
||||
@ -670,6 +673,7 @@ export const CreatePage = () => {
|
||||
}
|
||||
|
||||
const generateCreateSignature = async (
|
||||
markConfig: Mark[],
|
||||
fileHashes: {
|
||||
[key: string]: string
|
||||
},
|
||||
@ -677,7 +681,6 @@ export const CreatePage = () => {
|
||||
) => {
|
||||
const signers = users.filter((user) => user.role === UserRole.signer)
|
||||
const viewers = users.filter((user) => user.role === UserRole.viewer)
|
||||
const markConfig = createMarks(fileHashes)
|
||||
|
||||
const content: CreateSignatureEventContent = {
|
||||
signers: signers.map((signer) => hexToNpub(signer.pubkey)),
|
||||
@ -721,131 +724,152 @@ export const CreatePage = () => {
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
if (!validateInputs()) return
|
||||
try {
|
||||
if (!validateInputs()) return
|
||||
|
||||
setIsLoading(true)
|
||||
setLoadingSpinnerDesc('Generating file hashes')
|
||||
const fileHashes = await generateFileHashes()
|
||||
if (!fileHashes) {
|
||||
setIsLoading(true)
|
||||
setLoadingSpinnerDesc('Generating file hashes')
|
||||
const fileHashes = await generateFileHashes()
|
||||
if (!fileHashes) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Generating encryption key')
|
||||
const encryptionKey = await generateEncryptionKey()
|
||||
|
||||
if (await isOnline()) {
|
||||
setLoadingSpinnerDesc('generating files.zip')
|
||||
const arrayBuffer = await generateFilesZip()
|
||||
if (!arrayBuffer) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Encrypting files.zip')
|
||||
const encryptedArrayBuffer = await encryptZipFile(
|
||||
arrayBuffer,
|
||||
encryptionKey
|
||||
)
|
||||
|
||||
const markConfig = createMarks(fileHashes)
|
||||
|
||||
setLoadingSpinnerDesc('Uploading files.zip to file storage')
|
||||
const fileUrl = await uploadFile(encryptedArrayBuffer)
|
||||
if (!fileUrl) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Generating create signature')
|
||||
const createSignature = await generateCreateSignature(
|
||||
markConfig,
|
||||
fileHashes,
|
||||
fileUrl
|
||||
)
|
||||
if (!createSignature) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Generating keys for decryption')
|
||||
|
||||
// generate key pairs for decryption
|
||||
const pubkeys = users.map((user) => user.pubkey)
|
||||
// also add creator in the list
|
||||
if (pubkeys.includes(usersPubkey!)) {
|
||||
pubkeys.push(usersPubkey!)
|
||||
}
|
||||
|
||||
const keys = await generateKeys(pubkeys, encryptionKey)
|
||||
|
||||
if (!keys) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
const meta: Meta = {
|
||||
createSignature,
|
||||
keys,
|
||||
modifiedAt: unixNow(),
|
||||
docSignatures: {}
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Updating user app data')
|
||||
const event = await updateUsersAppData(meta)
|
||||
if (!event) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Sending notifications to counterparties')
|
||||
const promises = sendNotifications(meta)
|
||||
|
||||
await Promise.all(promises)
|
||||
.then(() => {
|
||||
toast.success('Notifications sent successfully')
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error('Failed to publish notifications')
|
||||
})
|
||||
|
||||
navigate(appPrivateRoutes.sign, { state: { meta: meta } })
|
||||
} else {
|
||||
const zip = new JSZip()
|
||||
|
||||
selectedFiles.forEach((file) => {
|
||||
zip.file(`files/${file.name}`, file)
|
||||
})
|
||||
|
||||
const markConfig = createMarks(fileHashes)
|
||||
|
||||
setLoadingSpinnerDesc('Generating create signature')
|
||||
const createSignature = await generateCreateSignature(
|
||||
markConfig,
|
||||
fileHashes,
|
||||
''
|
||||
)
|
||||
if (!createSignature) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
createSignature,
|
||||
modifiedAt: unixNow(),
|
||||
docSignatures: {}
|
||||
}
|
||||
|
||||
// add meta to zip
|
||||
try {
|
||||
const stringifiedMeta = JSON.stringify(meta, null, 2)
|
||||
zip.file('meta.json', stringifiedMeta)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
toast.error('An error occurred in converting meta json to string')
|
||||
return null
|
||||
}
|
||||
|
||||
const arrayBuffer = await generateZipFile(zip)
|
||||
if (!arrayBuffer) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Encrypting zip file')
|
||||
const encryptedArrayBuffer = await encryptZipFile(
|
||||
arrayBuffer,
|
||||
encryptionKey
|
||||
)
|
||||
|
||||
await handleOfflineFlow(encryptedArrayBuffer, encryptionKey)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
toast.error(error.message)
|
||||
}
|
||||
console.error(error)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Generating encryption key')
|
||||
const encryptionKey = await generateEncryptionKey()
|
||||
|
||||
if (await isOnline()) {
|
||||
setLoadingSpinnerDesc('generating files.zip')
|
||||
const arrayBuffer = await generateFilesZip()
|
||||
if (!arrayBuffer) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Encrypting files.zip')
|
||||
const encryptedArrayBuffer = await encryptZipFile(
|
||||
arrayBuffer,
|
||||
encryptionKey
|
||||
)
|
||||
|
||||
setLoadingSpinnerDesc('Uploading files.zip to file storage')
|
||||
const fileUrl = await uploadFile(encryptedArrayBuffer)
|
||||
if (!fileUrl) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Generating create signature')
|
||||
const createSignature = await generateCreateSignature(fileHashes, fileUrl)
|
||||
if (!createSignature) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Generating keys for decryption')
|
||||
|
||||
// generate key pairs for decryption
|
||||
const pubkeys = users.map((user) => user.pubkey)
|
||||
// also add creator in the list
|
||||
if (pubkeys.includes(usersPubkey!)) {
|
||||
pubkeys.push(usersPubkey!)
|
||||
}
|
||||
|
||||
const keys = await generateKeys(pubkeys, encryptionKey)
|
||||
|
||||
if (!keys) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
const meta: Meta = {
|
||||
createSignature,
|
||||
keys,
|
||||
modifiedAt: unixNow(),
|
||||
docSignatures: {}
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Updating user app data')
|
||||
const event = await updateUsersAppData(meta)
|
||||
if (!event) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Sending notifications to counterparties')
|
||||
const promises = sendNotifications(meta)
|
||||
|
||||
await Promise.all(promises)
|
||||
.then(() => {
|
||||
toast.success('Notifications sent successfully')
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error('Failed to publish notifications')
|
||||
})
|
||||
|
||||
navigate(appPrivateRoutes.sign, { state: { meta: meta } })
|
||||
} else {
|
||||
const zip = new JSZip()
|
||||
|
||||
selectedFiles.forEach((file) => {
|
||||
zip.file(`files/${file.name}`, file)
|
||||
})
|
||||
|
||||
setLoadingSpinnerDesc('Generating create signature')
|
||||
const createSignature = await generateCreateSignature(fileHashes, '')
|
||||
if (!createSignature) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
createSignature,
|
||||
modifiedAt: unixNow(),
|
||||
docSignatures: {}
|
||||
}
|
||||
|
||||
// add meta to zip
|
||||
try {
|
||||
const stringifiedMeta = JSON.stringify(meta, null, 2)
|
||||
zip.file('meta.json', stringifiedMeta)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
toast.error('An error occurred in converting meta json to string')
|
||||
return null
|
||||
}
|
||||
|
||||
const arrayBuffer = await generateZipFile(zip)
|
||||
if (!arrayBuffer) {
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingSpinnerDesc('Encrypting zip file')
|
||||
const encryptedArrayBuffer = await encryptZipFile(
|
||||
arrayBuffer,
|
||||
encryptionKey
|
||||
)
|
||||
|
||||
await handleOfflineFlow(encryptedArrayBuffer, encryptionKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ import { Container } from '../../components/Container'
|
||||
import { DisplayMeta } from './internal/displayMeta'
|
||||
import styles from './style.module.scss'
|
||||
import { CurrentUserMark, Mark } from '../../types/mark.ts'
|
||||
import { getLastSignersSig } from '../../utils/sign.ts'
|
||||
import { getLastSignersSig, isFullySigned } from '../../utils/sign.ts'
|
||||
import {
|
||||
filterMarksByPubkey,
|
||||
getCurrentUserMarks,
|
||||
@ -112,13 +112,13 @@ export const SignPage = () => {
|
||||
const [currentUserMarks, setCurrentUserMarks] = useState<CurrentUserMark[]>(
|
||||
[]
|
||||
)
|
||||
const [isReadyToSign, setIsReadyToSign] = useState(false)
|
||||
const [isMarksCompleted, setIsMarksCompleted] = useState(false)
|
||||
const [otherUserMarks, setOtherUserMarks] = useState<Mark[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (signers.length > 0) {
|
||||
// check if all signers have signed then its fully signed
|
||||
if (signers.every((signer) => signedBy.includes(signer))) {
|
||||
if (isFullySigned(signers, signedBy)) {
|
||||
setSignedStatus(SignedStatus.Fully_Signed)
|
||||
} else {
|
||||
for (const signer of signers) {
|
||||
@ -216,7 +216,7 @@ export const SignPage = () => {
|
||||
const otherUserMarks = findOtherUserMarks(signedMarks, usersPubkey!)
|
||||
setOtherUserMarks(otherUserMarks)
|
||||
setCurrentUserMarks(currentUserMarks)
|
||||
setIsReadyToSign(isCurrentUserMarksComplete(currentUserMarks))
|
||||
setIsMarksCompleted(isCurrentUserMarksComplete(currentUserMarks))
|
||||
}
|
||||
|
||||
setSignedBy(Object.keys(meta.docSignatures) as `npub1${string}`[])
|
||||
@ -542,10 +542,13 @@ export const SignPage = () => {
|
||||
setLoadingSpinnerDesc('Signing nostr event')
|
||||
|
||||
const prevSig = getPrevSignersSig(hexToNpub(usersPubkey!))
|
||||
if (!prevSig) return
|
||||
if (!prevSig) {
|
||||
setIsLoading(false)
|
||||
toast.error('Previous signature is invalid')
|
||||
return
|
||||
}
|
||||
|
||||
const marks = getSignerMarksForMeta()
|
||||
if (!marks) return
|
||||
const marks = getSignerMarksForMeta() || []
|
||||
|
||||
const signedEvent = await signEventForMeta({ prevSig, marks })
|
||||
if (!signedEvent) return
|
||||
@ -883,90 +886,90 @@ export const SignPage = () => {
|
||||
return <LoadingSpinner desc={loadingSpinnerDesc} />
|
||||
}
|
||||
|
||||
if (isReadyToSign) {
|
||||
if (!isMarksCompleted && signedStatus === SignedStatus.User_Is_Next_Signer) {
|
||||
return (
|
||||
<>
|
||||
<Container className={styles.container}>
|
||||
{displayInput && (
|
||||
<>
|
||||
<Typography component="label" variant="h6">
|
||||
Select sigit file
|
||||
</Typography>
|
||||
|
||||
<Box className={styles.inputBlock}>
|
||||
<MuiFileInput
|
||||
placeholder="Select file"
|
||||
inputProps={{ accept: '.sigit.zip' }}
|
||||
value={selectedFile}
|
||||
onChange={(value) => setSelectedFile(value)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{selectedFile && (
|
||||
<Box sx={{ mt: 2, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleDecrypt} variant="contained">
|
||||
Decrypt
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{submittedBy && Object.entries(files).length > 0 && meta && (
|
||||
<>
|
||||
<DisplayMeta
|
||||
meta={meta}
|
||||
files={files}
|
||||
submittedBy={submittedBy}
|
||||
signers={signers}
|
||||
viewers={viewers}
|
||||
creatorFileHashes={creatorFileHashes}
|
||||
currentFileHashes={currentFileHashes}
|
||||
signedBy={signedBy}
|
||||
nextSigner={nextSinger}
|
||||
getPrevSignersSig={getPrevSignersSig}
|
||||
/>
|
||||
|
||||
{signedStatus === SignedStatus.Fully_Signed && (
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleExport} variant="contained">
|
||||
Export
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{signedStatus === SignedStatus.User_Is_Next_Signer && (
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleSign} variant="contained">
|
||||
Sign
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{isSignerOrCreator && (
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleExportSigit} variant="contained">
|
||||
Export Sigit
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Container>
|
||||
</>
|
||||
<PdfMarking
|
||||
files={getCurrentUserFiles(files, currentFileHashes, creatorFileHashes)}
|
||||
currentUserMarks={currentUserMarks}
|
||||
setIsMarksCompleted={setIsMarksCompleted}
|
||||
setCurrentUserMarks={setCurrentUserMarks}
|
||||
setUpdatedMarks={setUpdatedMarks}
|
||||
handleDownload={handleDownload}
|
||||
otherUserMarks={otherUserMarks}
|
||||
meta={meta}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<PdfMarking
|
||||
files={getCurrentUserFiles(files, currentFileHashes, creatorFileHashes)}
|
||||
currentUserMarks={currentUserMarks}
|
||||
setIsReadyToSign={setIsReadyToSign}
|
||||
setCurrentUserMarks={setCurrentUserMarks}
|
||||
setUpdatedMarks={setUpdatedMarks}
|
||||
handleDownload={handleDownload}
|
||||
otherUserMarks={otherUserMarks}
|
||||
meta={meta}
|
||||
/>
|
||||
<>
|
||||
<Container className={styles.container}>
|
||||
{displayInput && (
|
||||
<>
|
||||
<Typography component="label" variant="h6">
|
||||
Select sigit file
|
||||
</Typography>
|
||||
|
||||
<Box className={styles.inputBlock}>
|
||||
<MuiFileInput
|
||||
placeholder="Select file"
|
||||
inputProps={{ accept: '.sigit.zip' }}
|
||||
value={selectedFile}
|
||||
onChange={(value) => setSelectedFile(value)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{selectedFile && (
|
||||
<Box sx={{ mt: 2, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleDecrypt} variant="contained">
|
||||
Decrypt
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{submittedBy && Object.entries(files).length > 0 && meta && (
|
||||
<>
|
||||
<DisplayMeta
|
||||
meta={meta}
|
||||
files={files}
|
||||
submittedBy={submittedBy}
|
||||
signers={signers}
|
||||
viewers={viewers}
|
||||
creatorFileHashes={creatorFileHashes}
|
||||
currentFileHashes={currentFileHashes}
|
||||
signedBy={signedBy}
|
||||
nextSigner={nextSinger}
|
||||
getPrevSignersSig={getPrevSignersSig}
|
||||
/>
|
||||
|
||||
{signedStatus === SignedStatus.Fully_Signed && (
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleExport} variant="contained">
|
||||
Export
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{signedStatus === SignedStatus.User_Is_Next_Signer && (
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleSign} variant="contained">
|
||||
Sign
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{isSignerOrCreator && (
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleExportSigit} variant="contained">
|
||||
Export Sigit
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
23
src/types/errors/MetaParseError.ts
Normal file
23
src/types/errors/MetaParseError.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Jsonable } from '.'
|
||||
|
||||
// Reuse common error messages for meta parsing
|
||||
export enum MetaParseErrorType {
|
||||
'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 class MetaParseError extends Error {
|
||||
public readonly context?: Jsonable
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
options: { cause?: Error; context?: Jsonable } = {}
|
||||
) {
|
||||
const { cause, context } = options
|
||||
|
||||
super(message, { cause })
|
||||
this.name = this.constructor.name
|
||||
|
||||
this.context = context
|
||||
}
|
||||
}
|
29
src/types/errors/index.ts
Normal file
29
src/types/errors/index.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export type Jsonable =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| undefined
|
||||
| readonly Jsonable[]
|
||||
| { readonly [key: string]: Jsonable }
|
||||
| { toJSON(): Jsonable }
|
||||
|
||||
/**
|
||||
* Handle errors
|
||||
* Wraps the errors without message property and stringify to a message so we can use it later
|
||||
* @param error
|
||||
* @returns
|
||||
*/
|
||||
export function handleError(error: unknown): Error {
|
||||
if (error instanceof Error) return error
|
||||
|
||||
// No message error, wrap it and stringify
|
||||
let stringified = 'Unable to stringify the thrown value'
|
||||
try {
|
||||
stringified = JSON.stringify(error)
|
||||
} catch (error) {
|
||||
console.error(stringified, error)
|
||||
}
|
||||
|
||||
return new Error(`Wrapped Error: ${stringified}`)
|
||||
}
|
@ -47,7 +47,7 @@ const filterMarksByPubkey = (marks: Mark[], pubkey: string): Mark[] => {
|
||||
|
||||
/**
|
||||
* Takes Signed Doc Signatures part of Meta and extracts
|
||||
* all Marks into one flar array, regardless of the user.
|
||||
* all Marks into one flat array, regardless of the user.
|
||||
* @param meta
|
||||
*/
|
||||
const extractMarksFromSignedMeta = (meta: Meta): Mark[] => {
|
||||
|
@ -3,6 +3,11 @@ 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,
|
||||
MetaParseErrorType
|
||||
} from '../types/errors/MetaParseError'
|
||||
|
||||
export enum SignStatus {
|
||||
Signed = 'Signed',
|
||||
@ -17,58 +22,6 @@ export enum SigitStatus {
|
||||
Complete = 'Completed'
|
||||
}
|
||||
|
||||
type Jsonable =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| undefined
|
||||
| readonly Jsonable[]
|
||||
| { readonly [key: string]: Jsonable }
|
||||
| { toJSON(): Jsonable }
|
||||
|
||||
export class SigitMetaParseError extends Error {
|
||||
public readonly context?: Jsonable
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
options: { cause?: Error; context?: Jsonable } = {}
|
||||
) {
|
||||
const { cause, context } = options
|
||||
|
||||
super(message, { cause })
|
||||
this.name = this.constructor.name
|
||||
|
||||
this.context = context
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle meta errors
|
||||
* Wraps the errors without message property and stringify to a message so we can use it later
|
||||
* @param error
|
||||
* @returns
|
||||
*/
|
||||
function handleError(error: unknown): Error {
|
||||
if (error instanceof Error) return error
|
||||
|
||||
// No message error, wrap it and stringify
|
||||
let stringified = 'Unable to stringify the thrown value'
|
||||
try {
|
||||
stringified = JSON.stringify(error)
|
||||
} catch (error) {
|
||||
console.error(stringified, error)
|
||||
}
|
||||
|
||||
return new Error(`[SiGit Error]: ${stringified}`)
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -89,7 +42,7 @@ export const parseNostrEvent = async (raw: string): Promise<Event> => {
|
||||
const event = await parseJson<Event>(raw)
|
||||
return event
|
||||
} catch (error) {
|
||||
throw new SigitMetaParseError(SigitMetaParseErrorType.PARSE_ERROR_EVENT, {
|
||||
throw new MetaParseError(MetaParseErrorType.PARSE_ERROR_EVENT, {
|
||||
cause: handleError(error),
|
||||
context: raw
|
||||
})
|
||||
@ -109,8 +62,8 @@ export const parseCreateSignatureEventContent = async (
|
||||
await parseJson<CreateSignatureEventContent>(raw)
|
||||
return createSignatureEventContent
|
||||
} catch (error) {
|
||||
throw new SigitMetaParseError(
|
||||
SigitMetaParseErrorType.PARSE_ERROR_SIGNATURE_EVENT_CONTENT,
|
||||
throw new MetaParseError(
|
||||
MetaParseErrorType.PARSE_ERROR_SIGNATURE_EVENT_CONTENT,
|
||||
{
|
||||
cause: handleError(error),
|
||||
context: raw
|
||||
@ -165,7 +118,7 @@ export const extractSigitCardDisplayInfo = async (meta: Meta) => {
|
||||
|
||||
return sigitInfo
|
||||
} catch (error) {
|
||||
if (error instanceof SigitMetaParseError) {
|
||||
if (error instanceof MetaParseError) {
|
||||
toast.error(error.message)
|
||||
console.error(error.name, error.message, error.cause, error.context)
|
||||
} else {
|
||||
|
@ -31,4 +31,16 @@ const getLastSignersSig = (
|
||||
}
|
||||
}
|
||||
|
||||
export { getLastSignersSig }
|
||||
/**
|
||||
* Checks if all signers have signed the sigit
|
||||
* @param signers - an array of npubs of all signers from the Sigit
|
||||
* @param signedBy - an array of npubs that have signed it already
|
||||
*/
|
||||
const isFullySigned = (
|
||||
signers: `npub1${string}`[],
|
||||
signedBy: `npub1${string}`[]
|
||||
): boolean => {
|
||||
return signers.every((signer) => signedBy.includes(signer))
|
||||
}
|
||||
|
||||
export { getLastSignersSig, isFullySigned }
|
||||
|
Loading…
Reference in New Issue
Block a user