From 0a0a9bef348e798d37d892b602e19e82e41d0fba Mon Sep 17 00:00:00 2001 From: Stixx Date: Fri, 6 Dec 2024 13:24:10 +0100 Subject: [PATCH 01/10] feat: Sign Directly From the Marking Screen fix: Marking inputs glitches, losing values --- package-lock.json | 27 ++ package.json | 1 + src/components/FileList/index.tsx | 43 +- src/components/MarkFormField/index.tsx | 97 ++++- .../MarkFormField/style.module.scss | 4 + src/components/PDFView/PdfMarking.tsx | 28 +- src/pages/sign/index.tsx | 389 ++++++------------ src/pages/verify/index.tsx | 123 +++++- src/utils/const.ts | 2 +- 9 files changed, 405 insertions(+), 309 deletions(-) diff --git a/package-lock.json b/package-lock.json index a03e759..ebeec88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "idb": "8.0.0", "jszip": "3.10.1", "lodash": "4.17.21", + "material-ui-popup-state": "^5.3.1", "mui-file-input": "4.0.4", "nostr-login": "^1.6.6", "nostr-tools": "2.7.0", @@ -3330,6 +3331,12 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -5976,6 +5983,26 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/material-ui-popup-state": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/material-ui-popup-state/-/material-ui-popup-state-5.3.1.tgz", + "integrity": "sha512-mmx1DsQwF/2cmcpHvS/QkUwOQG2oAM+cDEQU0DaZVYnvwKyTB3AFgu8l1/E+LQFausmzpSJoljwQSZXkNvt7eA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.6", + "@types/prop-types": "^15.7.3", + "@types/react": "^18.0.26", + "classnames": "^2.2.6", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@mui/material": "^5.0.0 || ^6.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", diff --git a/package.json b/package.json index ab49da0..562aadf 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "idb": "8.0.0", "jszip": "3.10.1", "lodash": "4.17.21", + "material-ui-popup-state": "^5.3.1", "mui-file-input": "4.0.4", "nostr-login": "^1.6.6", "nostr-tools": "2.7.0", diff --git a/src/components/FileList/index.tsx b/src/components/FileList/index.tsx index a47ace7..51ccdce 100644 --- a/src/components/FileList/index.tsx +++ b/src/components/FileList/index.tsx @@ -1,14 +1,17 @@ import { CurrentUserFile } from '../../types/file.ts' import styles from './style.module.scss' -import { Button } from '@mui/material' +import { Button, Menu, MenuItem } from '@mui/material' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCheck } from '@fortawesome/free-solid-svg-icons' +import PopupState, { bindTrigger, bindMenu } from 'material-ui-popup-state' +import React from 'react' interface FileListProps { files: CurrentUserFile[] currentFile: CurrentUserFile setCurrentFile: (file: CurrentUserFile) => void - handleDownload: () => void + handleExport: () => void + handleEncryptedExport?: () => void downloadLabel?: string } @@ -16,7 +19,8 @@ const FileList = ({ files, currentFile, setCurrentFile, - handleDownload, + handleExport, + handleEncryptedExport, downloadLabel }: FileListProps) => { const isActive = (file: CurrentUserFile) => file.id === currentFile.id @@ -42,9 +46,36 @@ const FileList = ({ ))} - + + {!downloadLabel && ( + + {(popupState) => ( + + + + { + popupState.close + handleExport() + }} + > + Export Files + + { + popupState.close + handleEncryptedExport && handleEncryptedExport() + }} + > + Export Encrypted Files + + + + )} + + )} ) } diff --git a/src/components/MarkFormField/index.tsx b/src/components/MarkFormField/index.tsx index 18bbf31..718a119 100644 --- a/src/components/MarkFormField/index.tsx +++ b/src/components/MarkFormField/index.tsx @@ -8,12 +8,14 @@ import { } from '../../utils' import React, { useState } from 'react' import { MarkInput } from '../MarkTypeStrategy/MarkInput.tsx' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCheck } from '@fortawesome/free-solid-svg-icons' interface MarkFormFieldProps { currentUserMarks: CurrentUserMark[] handleCurrentUserMarkChange: (mark: CurrentUserMark) => void handleSelectedMarkValueChange: (value: string) => void - handleSubmit: (event: React.FormEvent) => void + handleSubmit: (event: React.MouseEvent) => void selectedMark: CurrentUserMark selectedMarkValue: string } @@ -30,11 +32,13 @@ const MarkFormField = ({ handleCurrentUserMarkChange }: MarkFormFieldProps) => { const [displayActions, setDisplayActions] = useState(true) + const [complete, setComplete] = useState(false) + const isReadyToSign = () => isCurrentUserMarksComplete(currentUserMarks) || isCurrentValueLast(currentUserMarks, selectedMark, selectedMarkValue) const isCurrent = (currentMark: CurrentUserMark) => - currentMark.id === selectedMark.id + currentMark.id === selectedMark.id && !complete const isDone = (currentMark: CurrentUserMark) => isCurrent(currentMark) ? !!selectedMarkValue : currentMark.isCompleted const findNext = () => { @@ -46,13 +50,36 @@ const MarkFormField = ({ const handleFormSubmit = (event: React.FormEvent) => { event.preventDefault() console.log('handle form submit runs...') - return isReadyToSign() - ? handleSubmit(event) - : handleCurrentUserMarkChange(findNext()!) + + // Without this line, we lose mark values when switching + handleCurrentUserMarkChange(selectedMark) + + if (!complete) { + isReadyToSign() + ? setComplete(true) + : handleCurrentUserMarkChange(findNext()!) + } } + const toggleActions = () => setDisplayActions(!displayActions) const markLabel = getToolboxLabelByMarkType(selectedMark.mark.type) + const handleCurrentUserMarkClick = (mark: CurrentUserMark) => { + setComplete(false) + handleCurrentUserMarkChange(mark) + } + + const handleSelectCompleteMark = () => { + handleCurrentUserMarkChange(selectedMark) + setComplete(true) + } + + const handleSignAndComplete = ( + event: React.MouseEvent + ) => { + handleSubmit(event) + } + return (
@@ -78,25 +105,43 @@ const MarkFormField = ({
-

Add {markLabel}

+ {!complete && ( +

Add {markLabel}

+ )} + {complete &&

Finish

}
-
handleFormSubmit(e)}> - + {!complete && ( + handleFormSubmit(e)}> + +
+ +
+ + )} + + {complete && (
-
- + )} +
{currentUserMarks.map((mark, index) => { @@ -104,7 +149,7 @@ const MarkFormField = ({
@@ -114,6 +159,20 @@ const MarkFormField = ({
) })} +
+ + {complete && ( +
+ )} +
diff --git a/src/components/MarkFormField/style.module.scss b/src/components/MarkFormField/style.module.scss index 686595f..3825146 100644 --- a/src/components/MarkFormField/style.module.scss +++ b/src/components/MarkFormField/style.module.scss @@ -216,3 +216,7 @@ flex-direction: column; grid-gap: 5px; } + +.finishPage { + padding: 1px 0; +} diff --git a/src/components/PDFView/PdfMarking.tsx b/src/components/PDFView/PdfMarking.tsx index 222c393..f0d4f60 100644 --- a/src/components/PDFView/PdfMarking.tsx +++ b/src/components/PDFView/PdfMarking.tsx @@ -24,11 +24,12 @@ import { interface PdfMarkingProps { currentUserMarks: CurrentUserMark[] files: CurrentUserFile[] - handleDownload: () => void + handleExport: () => void + handleEncryptedExport: () => void + handleSign: () => void meta: Meta | null otherUserMarks: Mark[] setCurrentUserMarks: (currentUserMarks: CurrentUserMark[]) => void - setIsMarksCompleted: (isMarksCompleted: boolean) => void setUpdatedMarks: (markToUpdate: Mark) => void } @@ -42,10 +43,11 @@ const PdfMarking = (props: PdfMarkingProps) => { const { files, currentUserMarks, - setIsMarksCompleted, setCurrentUserMarks, setUpdatedMarks, - handleDownload, + handleExport, + handleEncryptedExport, + handleSign, meta, otherUserMarks } = props @@ -86,11 +88,18 @@ const PdfMarking = (props: PdfMarkingProps) => { updatedSelectedMark ) setCurrentUserMarks(updatedCurrentUserMarks) - setSelectedMarkValue(mark.currentValue ?? EMPTY) - setSelectedMark(mark) + + // If clicking on the same mark, don't update the value, otherwise do update + if (mark.id !== selectedMark.id) { + setSelectedMarkValue(mark.currentValue ?? EMPTY) + setSelectedMark(mark) + } } - const handleSubmit = (event: React.FormEvent) => { + /** + * Sign and Complete + */ + const handleSubmit = (event: React.MouseEvent) => { event.preventDefault() if (!selectedMarkValue || !selectedMark) return @@ -106,8 +115,8 @@ const PdfMarking = (props: PdfMarkingProps) => { ) setCurrentUserMarks(updatedCurrentUserMarks) setSelectedMark(null) - setIsMarksCompleted(true) setUpdatedMarks(updatedMark.mark) + handleSign() } // const updateCurrentUserMarkValues = () => { @@ -132,7 +141,8 @@ const PdfMarking = (props: PdfMarkingProps) => { files={files} currentFile={currentFile} setCurrentFile={setCurrentFile} - handleDownload={handleDownload} + handleExport={handleExport} + handleEncryptedExport={handleEncryptedExport} /> )}
diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx index f30ecdd..7636e61 100644 --- a/src/pages/sign/index.tsx +++ b/src/pages/sign/index.tsx @@ -1,17 +1,15 @@ -import { Box, Button, Typography } from '@mui/material' import axios from 'axios' import saveAs from 'file-saver' import JSZip from 'jszip' import _ from 'lodash' -import { MuiFileInput } from 'mui-file-input' import { Event, verifyEvent } from 'nostr-tools' import { useCallback, useEffect, useState } from 'react' -import { useAppSelector } from '../../hooks/store' +import { useAppSelector } from '../../hooks' import { useLocation, useNavigate, useParams } from 'react-router-dom' import { toast } from 'react-toastify' import { LoadingSpinner } from '../../components/LoadingSpinner' import { NostrController } from '../../controllers' -import { appPublicRoutes } from '../../routes' +import { appPrivateRoutes, appPublicRoutes } from '../../routes' import { CreateSignatureEventContent, Meta, SignedEvent } from '../../types' import { decryptArrayBuffer, @@ -36,15 +34,10 @@ import { timeout, processMarks } from '../../utils' -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, isFullySigned } from '../../utils/sign.ts' import { filterMarksByPubkey, getCurrentUserMarks, - isCurrentUserMarksComplete, updateMarks } from '../../utils' import PdfMarking from '../../components/PDFView/PdfMarking.tsx' @@ -53,16 +46,10 @@ import { getZipWithFiles, SigitFile } from '../../utils/file.ts' -import { ARRAY_BUFFER, DEFLATE } from '../../utils/const.ts' +import { ARRAY_BUFFER, DEFLATE } from '../../utils' import { generateTimestamp } from '../../utils/opentimestamps.ts' import { MARK_TYPE_CONFIG } from '../../components/MarkTypeStrategy/MarkStrategy.tsx' -enum SignedStatus { - Fully_Signed, - User_Is_Next_Signer, - User_Is_Not_Next_Signer -} - export const SignPage = () => { const navigate = useNavigate() const location = useLocation() @@ -100,17 +87,12 @@ export const SignPage = () => { } } - const [displayInput, setDisplayInput] = useState(false) - - const [selectedFile, setSelectedFile] = useState(null) - const [files, setFiles] = useState<{ [filename: string]: SigitFile }>({}) const [isLoading, setIsLoading] = useState(true) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') const [meta, setMeta] = useState(null) - const [signedStatus, setSignedStatus] = useState() const [submittedBy, setSubmittedBy] = useState() @@ -124,66 +106,14 @@ export const SignPage = () => { [key: string]: string | null }>({}) - const [signedBy, setSignedBy] = useState<`npub1${string}`[]>([]) - - const [nextSinger, setNextSinger] = useState() - - // This state variable indicates whether the logged-in user is a signer, a creator, or neither. - const [isSignerOrCreator, setIsSignerOrCreator] = useState(false) - const usersPubkey = useAppSelector((state) => state.auth.usersPubkey) const nostrController = NostrController.getInstance() const [currentUserMarks, setCurrentUserMarks] = useState( [] ) - const [isMarksCompleted, setIsMarksCompleted] = useState(false) const [otherUserMarks, setOtherUserMarks] = useState([]) - useEffect(() => { - if (signers.length > 0) { - // check if all signers have signed then its fully signed - if (isFullySigned(signers, signedBy)) { - setSignedStatus(SignedStatus.Fully_Signed) - } else { - for (const signer of signers) { - if (!signedBy.includes(signer)) { - // signers in meta.json are in npub1 format - // so, convert it to hex before setting to nextSigner - setNextSinger(npubToHex(signer)!) - - const usersNpub = hexToNpub(usersPubkey!) - - if (signer === usersNpub) { - // logged in user is the next signer - setSignedStatus(SignedStatus.User_Is_Next_Signer) - } else { - setSignedStatus(SignedStatus.User_Is_Not_Next_Signer) - } - - break - } - } - } - } else { - // there's no signer just viewers. So its fully signed - setSignedStatus(SignedStatus.Fully_Signed) - } - - // Determine and set the status of the user - if (submittedBy && usersPubkey && submittedBy === usersPubkey) { - // If the submission was made by the user, set the status to true - setIsSignerOrCreator(true) - } else if (usersPubkey) { - // Convert the user's public key from hex to npub format - const usersNpub = hexToNpub(usersPubkey) - if (signers.includes(usersNpub)) { - // If the user's npub is in the list of signers, set the status to true - setIsSignerOrCreator(true) - } - } - }, [signers, signedBy, usersPubkey, submittedBy]) - useEffect(() => { const handleUpdatedMeta = async (meta: Meta) => { const createSignatureEvent = await parseJson( @@ -278,10 +208,7 @@ export const SignPage = () => { setOtherUserMarks(otherUserMarks) setCurrentUserMarks(currentUserMarks) - setIsMarksCompleted(isCurrentUserMarksComplete(currentUserMarks)) } - - setSignedBy(Object.keys(meta.docSignatures) as `npub1${string}`[]) } if (meta) { @@ -290,29 +217,6 @@ export const SignPage = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [meta, usersPubkey]) - const handleDownload = async () => { - if (Object.entries(files).length === 0 || !meta || !usersPubkey) return - setLoadingSpinnerDesc('Generating file') - try { - const zip = await getZipWithFiles(meta, files) - const arrayBuffer = await zip.generateAsync({ - type: ARRAY_BUFFER, - compression: DEFLATE, - compressionOptions: { - level: 6 - } - }) - if (!arrayBuffer) return - const blob = new Blob([arrayBuffer]) - saveAs(blob, `exported-${unixNow()}.sigit.zip`) - } catch (error) { - console.log('error in zip:>> ', error) - if (error instanceof Error) { - toast.error(error.message || 'Error occurred in generating zip file') - } - } - } - const decrypt = useCallback( async (file: File) => { setLoadingSpinnerDesc('Decrypting file') @@ -424,7 +328,6 @@ export const SignPage = () => { }) } else { setIsLoading(false) - setDisplayInput(true) } }, [decryptedArrayBuffer, uploadedZip, metaInNavState, decrypt]) @@ -541,9 +444,6 @@ export const SignPage = () => { setFiles(files) setCurrentFileHashes(fileHashes) - - setDisplayInput(false) - setLoadingSpinnerDesc('Parsing meta.json') const metaFileContent = await readContentOfZipEntry( @@ -571,21 +471,6 @@ export const SignPage = () => { setMeta(parsedMetaJson) } - const handleDecrypt = async () => { - if (!selectedFile) return - - setIsLoading(true) - const arrayBuffer = await decrypt(selectedFile) - - if (!arrayBuffer) { - setIsLoading(false) - toast.error('Error decrypting file') - return - } - - handleDecryptedArrayBuffer(arrayBuffer) - } - const handleSign = async () => { if (Object.entries(files).length === 0 || !meta) return @@ -640,6 +525,13 @@ export const SignPage = () => { setMeta(updatedMeta) setIsLoading(false) } + + if (metaInNavState) { + const createSignature = JSON.parse(metaInNavState.createSignature) + navigate(`${appPublicRoutes.verify}/${createSignature.id}`) + } else { + navigate(appPrivateRoutes.homePage) + } } // Sign the event for the meta file @@ -730,6 +622,14 @@ export const SignPage = () => { }) } + // Check if the current user is the last signer + const checkIsLastSigner = (signers: string[]): boolean => { + const usersNpub = hexToNpub(usersPubkey!) + const lastSignerIndex = signers.length - 1 + const signerIndex = signers.indexOf(usersNpub) + return signerIndex === lastSignerIndex + } + // Handle errors during zip file generation const handleZipError = (err: unknown) => { console.log('Error in zip:>> ', err) @@ -740,7 +640,7 @@ export const SignPage = () => { return null } - // Handle the online flow: update users app data and send notifications + // Handle the online flow: update users' app data and send notifications const handleOnlineFlow = async (meta: Meta) => { setLoadingSpinnerDesc('Updating users app data') const updatedEvent = await updateUsersAppData(meta) @@ -795,82 +695,102 @@ export const SignPage = () => { setIsLoading(false) } - // Check if the current user is the last signer - const checkIsLastSigner = (signers: string[]): boolean => { - const usersNpub = hexToNpub(usersPubkey!) - const lastSignerIndex = signers.length - 1 - const signerIndex = signers.indexOf(usersNpub) - return signerIndex === lastSignerIndex - } - const handleExport = async () => { if (Object.entries(files).length === 0 || !meta || !usersPubkey) return - - const usersNpub = hexToNpub(usersPubkey) - if ( - !signers.includes(usersNpub) && - !viewers.includes(usersNpub) && - submittedBy !== usersNpub - ) - return - - setIsLoading(true) - setLoadingSpinnerDesc('Signing nostr event') - - if (!meta) return - - const prevSig = getLastSignersSig(meta, signers) - if (!prevSig) return - - const signedEvent = await signEventForMetaFile( - JSON.stringify({ - prevSig - }), - nostrController, - setIsLoading - ) - - if (!signedEvent) return - - const exportSignature = JSON.stringify(signedEvent, null, 2) - - const stringifiedMeta = JSON.stringify( - { - ...meta, - exportSignature - }, - null, - 2 - ) - - const zip = await getZipWithFiles(meta, files) - zip.file('meta.json', stringifiedMeta) - - const arrayBuffer = await zip - .generateAsync({ - type: 'arraybuffer', - compression: 'DEFLATE', + setLoadingSpinnerDesc('Generating file') + try { + const zip = await getZipWithFiles(meta, files) + const arrayBuffer = await zip.generateAsync({ + type: ARRAY_BUFFER, + compression: DEFLATE, compressionOptions: { level: 6 } }) - .catch((err) => { - console.log('err in zip:>> ', err) - setIsLoading(false) - toast.error(err.message || 'Error occurred in generating zip file') - return null - }) - - if (!arrayBuffer) return - - const blob = new Blob([arrayBuffer]) - saveAs(blob, `exported-${unixNow()}.sigit.zip`) - - setIsLoading(false) - - navigate(appPublicRoutes.verify) + if (!arrayBuffer) return + const blob = new Blob([arrayBuffer]) + saveAs(blob, `exported-${unixNow()}.sigit.zip`) + } catch (error) { + console.log('error in zip:>> ', error) + if (error instanceof Error) { + toast.error(error.message || 'Error occurred in generating zip file') + } + } } + /** + * @Depricated + * It's the same as {@link handleExport} but the code is not the same + * leaving it for the reference + */ + // const handleExport = async () => { + // if (Object.entries(files).length === 0 || !meta || !usersPubkey) return + // + // const usersNpub = hexToNpub(usersPubkey) + // if ( + // !signers.includes(usersNpub) && + // !viewers.includes(usersNpub) && + // submittedBy !== usersNpub + // ) + // return + // + // setIsLoading(true) + // setLoadingSpinnerDesc('Signing nostr event') + // + // if (!meta) return + // + // const prevSig = getLastSignersSig(meta, signers) + // if (!prevSig) return + // + // const signedEvent = await signEventForMetaFile( + // JSON.stringify({ + // prevSig + // }), + // nostrController, + // setIsLoading + // ) + // + // if (!signedEvent) return + // + // const exportSignature = JSON.stringify(signedEvent, null, 2) + // + // const stringifiedMeta = JSON.stringify( + // { + // ...meta, + // exportSignature + // }, + // null, + // 2 + // ) + // + // const zip = await getZipWithFiles(meta, files) + // zip.file('meta.json', stringifiedMeta) + // + // const arrayBuffer = await zip + // .generateAsync({ + // type: 'arraybuffer', + // compression: 'DEFLATE', + // compressionOptions: { + // level: 6 + // } + // }) + // .catch((err) => { + // console.log('err in zip:>> ', err) + // setIsLoading(false) + // toast.error(err.message || 'Error occurred in generating zip file') + // return null + // }) + // + // if (!arrayBuffer) return + // + // const blob = new Blob([arrayBuffer]) + // saveAs(blob, `exported-${unixNow()}.sigit.zip`) + // + // setIsLoading(false) + // + // navigate(appPublicRoutes.verify) + // } + const handleEncryptedExport = async () => { if (Object.entries(files).length === 0 || !meta) return @@ -944,90 +864,17 @@ export const SignPage = () => { return } - if (!isMarksCompleted && signedStatus === SignedStatus.User_Is_Next_Signer) { - return ( - - ) - } - return ( - <> - - {displayInput && ( - <> - - Select sigit file - - - - setSelectedFile(value)} - /> - - - {selectedFile && ( - - - - )} - - )} - - {submittedBy && Object.entries(files).length > 0 && meta && ( - <> - - - {signedStatus === SignedStatus.Fully_Signed && ( - - - - )} - - {signedStatus === SignedStatus.User_Is_Next_Signer && ( - - - - )} - - {isSignerOrCreator && ( - - - - )} - - )} - - + ) } diff --git a/src/pages/verify/index.tsx b/src/pages/verify/index.tsx index 515a257..1d91fa4 100644 --- a/src/pages/verify/index.tsx +++ b/src/pages/verify/index.tsx @@ -23,7 +23,10 @@ import { getCurrentUserFiles, updateUsersAppData, npubToHex, - sendNotification + sendNotification, + generateEncryptionKey, + encryptArrayBuffer, + generateKeysFile } from '../../utils' import styles from './style.module.scss' import { useLocation, useParams } from 'react-router-dom' @@ -597,6 +600,120 @@ export const VerifyPage = () => { setIsLoading(false) } + // Handle errors during zip file generation + const handleZipError = (err: unknown) => { + console.log('Error in zip:>> ', err) + setIsLoading(false) + if (err instanceof Error) { + toast.error(err.message || 'Error occurred in generating zip file') + } + return null + } + + // Check if the current user is the last signer + const checkIsLastSigner = (signers: string[]): boolean => { + const usersNpub = hexToNpub(usersPubkey!) + const lastSignerIndex = signers.length - 1 + const signerIndex = signers.indexOf(usersNpub) + return signerIndex === lastSignerIndex + } + + // create final zip file + const createFinalZipFile = async ( + encryptedArrayBuffer: ArrayBuffer, + encryptionKey: string + ): Promise => { + // Get the current timestamp in seconds + const blob = new Blob([encryptedArrayBuffer]) + // Create a File object with the Blob data + const file = new File([blob], `compressed.sigit`, { + type: 'application/sigit' + }) + + const isLastSigner = checkIsLastSigner(signers) + + const userSet = new Set() + + if (isLastSigner) { + if (submittedBy) { + userSet.add(submittedBy) + } + + signers.forEach((signer) => { + userSet.add(npubToHex(signer)!) + }) + + viewers.forEach((viewer) => { + userSet.add(npubToHex(viewer)!) + }) + } else { + const usersNpub = hexToNpub(usersPubkey!) + const signerIndex = signers.indexOf(usersNpub) + const nextSigner = signers[signerIndex + 1] + userSet.add(npubToHex(nextSigner)!) + } + + const keysFileContent = await generateKeysFile( + Array.from(userSet), + encryptionKey + ) + if (!keysFileContent) return null + + const zip = new JSZip() + zip.file(`compressed.sigit`, file) + zip.file('keys.json', keysFileContent) + + const arraybuffer = await zip + .generateAsync({ + type: 'arraybuffer', + compression: 'DEFLATE', + compressionOptions: { level: 6 } + }) + .catch(handleZipError) + + if (!arraybuffer) return null + + return new File([new Blob([arraybuffer])], `${unixNow()}.sigit.zip`, { + type: 'application/zip' + }) + } + + const handleEncryptedExport = async () => { + if (Object.entries(files).length === 0 || !meta) return + + const stringifiedMeta = JSON.stringify(meta, null, 2) + const zip = await getZipWithFiles(meta, files) + + zip.file('meta.json', stringifiedMeta) + + const arrayBuffer = await zip + .generateAsync({ + type: 'arraybuffer', + compression: 'DEFLATE', + compressionOptions: { + level: 6 + } + }) + .catch((err) => { + console.log('err in zip:>> ', err) + setIsLoading(false) + toast.error(err.message || 'Error occurred in generating zip file') + return null + }) + + if (!arrayBuffer) return + + const key = await generateEncryptionKey() + + setLoadingSpinnerDesc('Encrypting zip file') + const encryptedArrayBuffer = await encryptArrayBuffer(arrayBuffer, key) + + const finalZipFile = await createFinalZipFile(encryptedArrayBuffer, key) + + if (!finalZipFile) return + saveAs(finalZipFile, `exported-${unixNow()}.sigit.zip`) + } + return ( <> {isLoading && } @@ -640,8 +757,8 @@ export const VerifyPage = () => { )} currentFile={currentFile} setCurrentFile={setCurrentFile} - handleDownload={handleMarkedExport} - downloadLabel="Download Sigit" + handleExport={handleMarkedExport} + handleEncryptedExport={handleEncryptedExport} /> ) } diff --git a/src/utils/const.ts b/src/utils/const.ts index 522f242..2b8e822 100644 --- a/src/utils/const.ts +++ b/src/utils/const.ts @@ -120,5 +120,5 @@ export const SIGNATURE_PAD_OPTIONS = { export const SIGNATURE_PAD_SIZE = { width: 300, - height: 300 + height: 150 } From d0231b0652c8db805d1e9b84dfbf22528419612a Mon Sep 17 00:00:00 2001 From: Stixx Date: Fri, 6 Dec 2024 14:41:44 +0100 Subject: [PATCH 02/10] chore: typo --- src/pages/sign/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx index 7636e61..ab95318 100644 --- a/src/pages/sign/index.tsx +++ b/src/pages/sign/index.tsx @@ -719,7 +719,7 @@ export const SignPage = () => { } /** - * @Depricated + * @deprecated * It's the same as {@link handleExport} but the code is not the same * leaving it for the reference */ From 69efd9e09d043403c987a5ab7fddd68158a1d22a Mon Sep 17 00:00:00 2001 From: Stixx Date: Fri, 6 Dec 2024 15:49:57 +0100 Subject: [PATCH 03/10] fix: clicking logo not redirecting to home --- src/components/AppBar/AppBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index a4f653e..e7f5d95 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -125,7 +125,7 @@ export const AppBar = () => { src="/logo.svg" alt="Logo" onClick={() => { - if (window.location.pathname === '/') { + if (['', '#/'].includes(window.location.hash)) { location.reload() } else { navigate('/') From 0fd0f26fc7ef87aea7c48ccddae822d23d2b8853 Mon Sep 17 00:00:00 2001 From: Stixx Date: Fri, 6 Dec 2024 16:22:23 +0100 Subject: [PATCH 04/10] fix: counterpart search NIP05 glitching --- src/pages/create/index.tsx | 41 ++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index 212f7bf..dfe3338 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -148,6 +148,17 @@ export const CreatePage = () => { [setUserInput] ) + const handleSearchUserNip05 = async ( + nip05: string + ): Promise => { + const { pubkey } = await queryNip05(nip05).catch((err) => { + console.error(err) + return { pubkey: null } + }) + + return pubkey + } + const handleSearchUsers = async (searchValue?: string) => { const searchString = searchValue || userSearchInput || undefined @@ -224,7 +235,9 @@ export const CreatePage = () => { }) }, [foundUsers]) - const handleInputKeyDown = (event: React.KeyboardEvent) => { + const handleInputKeyDown = async ( + event: React.KeyboardEvent + ) => { if ( event.code === KeyboardCode.Enter || event.code === KeyboardCode.NumpadEnter @@ -238,7 +251,18 @@ export const CreatePage = () => { } else { // Otherwize if search already provided some results, user must manually click the search button if (!foundUsers.length) { - handleSearchUsers() + // If it's NIP05 send request to .well-known + if (userSearchInput.includes('@')) { + const pubkey = await handleSearchUserNip05(userSearchInput) + + if (pubkey) { + setUserInput(userSearchInput) + } else { + toast.error(`No user found with the NIP05: ${userSearchInput}`) + } + } else { + handleSearchUsers() + } } } } @@ -959,19 +983,6 @@ export const CreatePage = () => { } else { disarmAddOnEnter() } - } else if (value.includes('@')) { - // Seems like it's nip05 format - const { pubkey } = await queryNip05(value).catch((err) => { - console.error(err) - return { pubkey: null } - }) - - if (pubkey) { - // Arm the manual user npub add after enter is hit, we don't want to trigger search - setPastedUserNpubOrNip05(hexToNpub(pubkey)) - } else { - disarmAddOnEnter() - } } else { // Disarm the add user on enter hit, and trigger search after 1 second disarmAddOnEnter() From 7b29d7055eeb6bf7f9bbcc06fb2ecf0962157046 Mon Sep 17 00:00:00 2001 From: Stixx Date: Fri, 6 Dec 2024 20:58:20 +0100 Subject: [PATCH 05/10] fix: search counterparts nip05 does not need to include '@' --- src/pages/create/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index dfe3338..1defde7 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -251,10 +251,15 @@ export const CreatePage = () => { } else { // Otherwize if search already provided some results, user must manually click the search button if (!foundUsers.length) { - // If it's NIP05 send request to .well-known - if (userSearchInput.includes('@')) { + // If it's NIP05 (includes @ or is a valid domain) send request to .well-known + const domainRegex = /^[a-zA-Z0-9@.-]+\.[a-zA-Z]{2,}$/ + if (domainRegex.test(userSearchInput)) { + setSearchUsersLoading(true) + const pubkey = await handleSearchUserNip05(userSearchInput) + setSearchUsersLoading(false) + if (pubkey) { setUserInput(userSearchInput) } else { From afbe05b4c88d5042c17f4a59c0739dc8942642a9 Mon Sep 17 00:00:00 2001 From: Stixx Date: Fri, 6 Dec 2024 21:00:52 +0100 Subject: [PATCH 06/10] fix: footer 'Home' button scroll to top when on home page, fixed logic --- src/components/Footer/Footer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 249b8c4..c7459e3 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -69,7 +69,7 @@ export const Footer = () => component={Link} to={'/'} onClick={(event) => { - if (window.location.pathname === '/') { + if (['', '#/'].includes(window.location.hash)) { event.preventDefault() window.scrollTo(0, 0) } From 8d8c38e90bce13c3a0b833a5598a790c2086a267 Mon Sep 17 00:00:00 2001 From: Stixx Date: Mon, 9 Dec 2024 22:37:27 +0100 Subject: [PATCH 07/10] fix: addressing comments --- src/components/FileList/index.tsx | 61 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/components/FileList/index.tsx b/src/components/FileList/index.tsx index 51ccdce..a073ca4 100644 --- a/src/components/FileList/index.tsx +++ b/src/components/FileList/index.tsx @@ -12,7 +12,6 @@ interface FileListProps { setCurrentFile: (file: CurrentUserFile) => void handleExport: () => void handleEncryptedExport?: () => void - downloadLabel?: string } const FileList = ({ @@ -20,8 +19,7 @@ const FileList = ({ currentFile, setCurrentFile, handleExport, - handleEncryptedExport, - downloadLabel + handleEncryptedExport }: FileListProps) => { const isActive = (file: CurrentUserFile) => file.id === currentFile.id return ( @@ -47,35 +45,34 @@ const FileList = ({ ))} - {!downloadLabel && ( - - {(popupState) => ( - - - - { - popupState.close - handleExport() - }} - > - Export Files - - { - popupState.close - handleEncryptedExport && handleEncryptedExport() - }} - > - Export Encrypted Files - - - - )} - - )} + + {(popupState) => ( + + + + { + popupState.close + handleExport() + }} + > + Export Files + + { + popupState.close + typeof handleEncryptedExport === 'function' && + handleEncryptedExport() + }} + > + Export Encrypted Files + + + + )} +
) } From 6716c3da636f9c0a1b168cb2cb0a98da97a6c5c3 Mon Sep 17 00:00:00 2001 From: Stixx Date: Tue, 10 Dec 2024 14:42:12 +0100 Subject: [PATCH 08/10] fix: including signatures in both export and encrypted export --- src/pages/sign/index.tsx | 197 +++++++++++++++---------------------- src/pages/verify/index.tsx | 151 +++++++++++++--------------- 2 files changed, 151 insertions(+), 197 deletions(-) diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx index ab95318..16fa3c4 100644 --- a/src/pages/sign/index.tsx +++ b/src/pages/sign/index.tsx @@ -32,7 +32,8 @@ import { updateUsersAppData, findOtherUserMarks, timeout, - processMarks + processMarks, + ARRAY_BUFFER } from '../../utils' import { CurrentUserMark, Mark } from '../../types/mark.ts' import { @@ -46,9 +47,10 @@ import { getZipWithFiles, SigitFile } from '../../utils/file.ts' -import { ARRAY_BUFFER, DEFLATE } from '../../utils' +import { DEFLATE } from '../../utils' import { generateTimestamp } from '../../utils/opentimestamps.ts' import { MARK_TYPE_CONFIG } from '../../components/MarkTypeStrategy/MarkStrategy.tsx' +import { getLastSignersSig } from '../../utils/sign.ts' export const SignPage = () => { const navigate = useNavigate() @@ -696,124 +698,19 @@ export const SignPage = () => { } const handleExport = async () => { - if (Object.entries(files).length === 0 || !meta || !usersPubkey) return - setLoadingSpinnerDesc('Generating file') - try { - const zip = await getZipWithFiles(meta, files) - const arrayBuffer = await zip.generateAsync({ - type: ARRAY_BUFFER, - compression: DEFLATE, - compressionOptions: { - level: 6 - } - }) - if (!arrayBuffer) return - const blob = new Blob([arrayBuffer]) - saveAs(blob, `exported-${unixNow()}.sigit.zip`) - } catch (error) { - console.log('error in zip:>> ', error) - if (error instanceof Error) { - toast.error(error.message || 'Error occurred in generating zip file') - } - } + const arrayBuffer = await prepareZipExport() + if (!arrayBuffer) return + + const blob = new Blob([arrayBuffer]) + saveAs(blob, `exported-${unixNow()}.sigit.zip`) + + setIsLoading(false) + + navigate(appPublicRoutes.verify) } - /** - * @deprecated - * It's the same as {@link handleExport} but the code is not the same - * leaving it for the reference - */ - // const handleExport = async () => { - // if (Object.entries(files).length === 0 || !meta || !usersPubkey) return - // - // const usersNpub = hexToNpub(usersPubkey) - // if ( - // !signers.includes(usersNpub) && - // !viewers.includes(usersNpub) && - // submittedBy !== usersNpub - // ) - // return - // - // setIsLoading(true) - // setLoadingSpinnerDesc('Signing nostr event') - // - // if (!meta) return - // - // const prevSig = getLastSignersSig(meta, signers) - // if (!prevSig) return - // - // const signedEvent = await signEventForMetaFile( - // JSON.stringify({ - // prevSig - // }), - // nostrController, - // setIsLoading - // ) - // - // if (!signedEvent) return - // - // const exportSignature = JSON.stringify(signedEvent, null, 2) - // - // const stringifiedMeta = JSON.stringify( - // { - // ...meta, - // exportSignature - // }, - // null, - // 2 - // ) - // - // const zip = await getZipWithFiles(meta, files) - // zip.file('meta.json', stringifiedMeta) - // - // const arrayBuffer = await zip - // .generateAsync({ - // type: 'arraybuffer', - // compression: 'DEFLATE', - // compressionOptions: { - // level: 6 - // } - // }) - // .catch((err) => { - // console.log('err in zip:>> ', err) - // setIsLoading(false) - // toast.error(err.message || 'Error occurred in generating zip file') - // return null - // }) - // - // if (!arrayBuffer) return - // - // const blob = new Blob([arrayBuffer]) - // saveAs(blob, `exported-${unixNow()}.sigit.zip`) - // - // setIsLoading(false) - // - // navigate(appPublicRoutes.verify) - // } - const handleEncryptedExport = async () => { - if (Object.entries(files).length === 0 || !meta) return - - const stringifiedMeta = JSON.stringify(meta, null, 2) - const zip = await getZipWithFiles(meta, files) - - zip.file('meta.json', stringifiedMeta) - - const arrayBuffer = await zip - .generateAsync({ - type: 'arraybuffer', - compression: 'DEFLATE', - compressionOptions: { - level: 6 - } - }) - .catch((err) => { - console.log('err in zip:>> ', err) - setIsLoading(false) - toast.error(err.message || 'Error occurred in generating zip file') - return null - }) - + const arrayBuffer = await prepareZipExport() if (!arrayBuffer) return const key = await generateEncryptionKey() @@ -825,6 +722,72 @@ export const SignPage = () => { if (!finalZipFile) return saveAs(finalZipFile, `exported-${unixNow()}.sigit.zip`) + + setIsLoading(false) + } + + const prepareZipExport = async (): Promise => { + if (Object.entries(files).length === 0 || !meta || !usersPubkey) + return Promise.resolve(null) + + const usersNpub = hexToNpub(usersPubkey) + if ( + !signers.includes(usersNpub) && + !viewers.includes(usersNpub) && + submittedBy !== usersNpub + ) + return Promise.resolve(null) + + setIsLoading(true) + setLoadingSpinnerDesc('Signing nostr event') + + if (!meta) return Promise.resolve(null) + + const prevSig = getLastSignersSig(meta, signers) + if (!prevSig) return Promise.resolve(null) + + const signedEvent = await signEventForMetaFile( + JSON.stringify({ + prevSig + }), + nostrController, + setIsLoading + ) + + if (!signedEvent) return Promise.resolve(null) + + const exportSignature = JSON.stringify(signedEvent, null, 2) + + const stringifiedMeta = JSON.stringify( + { + ...meta, + exportSignature + }, + null, + 2 + ) + + const zip = await getZipWithFiles(meta, files) + zip.file('meta.json', stringifiedMeta) + + const arrayBuffer = await zip + .generateAsync({ + type: ARRAY_BUFFER, + compression: DEFLATE, + compressionOptions: { + level: 6 + } + }) + .catch((err) => { + console.log('err in zip:>> ', err) + setIsLoading(false) + toast.error(err.message || 'Error occurred in generating zip file') + return null + }) + + if (!arrayBuffer) return Promise.resolve(null) + + return Promise.resolve(arrayBuffer) } /** diff --git a/src/pages/verify/index.tsx b/src/pages/verify/index.tsx index 1d91fa4..1aedc4d 100644 --- a/src/pages/verify/index.tsx +++ b/src/pages/verify/index.tsx @@ -26,7 +26,9 @@ import { sendNotification, generateEncryptionKey, encryptArrayBuffer, - generateKeysFile + generateKeysFile, + ARRAY_BUFFER, + DEFLATE } from '../../utils' import styles from './style.module.scss' import { useLocation, useParams } from 'react-router-dom' @@ -544,62 +546,6 @@ export const VerifyPage = () => { setIsLoading(false) } - const handleMarkedExport = async () => { - if (Object.entries(files).length === 0 || !meta || !usersPubkey) return - - const usersNpub = hexToNpub(usersPubkey) - if ( - !signers.includes(usersNpub) && - !viewers.includes(usersNpub) && - submittedBy !== usersNpub - ) { - return - } - - setIsLoading(true) - setLoadingSpinnerDesc('Signing nostr event') - - const prevSig = getLastSignersSig(meta, signers) - if (!prevSig) return - - const signedEvent = await signEventForMetaFile( - JSON.stringify({ prevSig }), - nostrController, - setIsLoading - ) - - if (!signedEvent) return - - const exportSignature = JSON.stringify(signedEvent, null, 2) - const updatedMeta = { ...meta, exportSignature } - const stringifiedMeta = JSON.stringify(updatedMeta, null, 2) - - const zip = await getZipWithFiles(updatedMeta, files) - zip.file('meta.json', stringifiedMeta) - - const arrayBuffer = await zip - .generateAsync({ - type: 'arraybuffer', - compression: 'DEFLATE', - compressionOptions: { - level: 6 - } - }) - .catch((err) => { - console.log('err in zip:>> ', err) - setIsLoading(false) - toast.error(err.message || 'Error occurred in generating zip file') - return null - }) - - if (!arrayBuffer) return - - const blob = new Blob([arrayBuffer]) - saveAs(blob, `exported-${unixNow()}.sigit.zip`) - - setIsLoading(false) - } - // Handle errors during zip file generation const handleZipError = (err: unknown) => { console.log('Error in zip:>> ', err) @@ -678,29 +624,18 @@ export const VerifyPage = () => { }) } + const handleExport = async () => { + const arrayBuffer = await prepareZipExport() + if (!arrayBuffer) return + + const blob = new Blob([arrayBuffer]) + saveAs(blob, `exported-${unixNow()}.sigit.zip`) + + setIsLoading(false) + } + const handleEncryptedExport = async () => { - if (Object.entries(files).length === 0 || !meta) return - - const stringifiedMeta = JSON.stringify(meta, null, 2) - const zip = await getZipWithFiles(meta, files) - - zip.file('meta.json', stringifiedMeta) - - const arrayBuffer = await zip - .generateAsync({ - type: 'arraybuffer', - compression: 'DEFLATE', - compressionOptions: { - level: 6 - } - }) - .catch((err) => { - console.log('err in zip:>> ', err) - setIsLoading(false) - toast.error(err.message || 'Error occurred in generating zip file') - return null - }) - + const arrayBuffer = await prepareZipExport() if (!arrayBuffer) return const key = await generateEncryptionKey() @@ -712,6 +647,62 @@ export const VerifyPage = () => { if (!finalZipFile) return saveAs(finalZipFile, `exported-${unixNow()}.sigit.zip`) + + setIsLoading(false) + } + + const prepareZipExport = async (): Promise => { + if (Object.entries(files).length === 0 || !meta || !usersPubkey) + return Promise.resolve(null) + + const usersNpub = hexToNpub(usersPubkey) + if ( + !signers.includes(usersNpub) && + !viewers.includes(usersNpub) && + submittedBy !== usersNpub + ) { + return Promise.resolve(null) + } + + setIsLoading(true) + setLoadingSpinnerDesc('Signing nostr event') + + const prevSig = getLastSignersSig(meta, signers) + if (!prevSig) return Promise.resolve(null) + + const signedEvent = await signEventForMetaFile( + JSON.stringify({ prevSig }), + nostrController, + setIsLoading + ) + + if (!signedEvent) return Promise.resolve(null) + + const exportSignature = JSON.stringify(signedEvent, null, 2) + const updatedMeta = { ...meta, exportSignature } + const stringifiedMeta = JSON.stringify(updatedMeta, null, 2) + + const zip = await getZipWithFiles(updatedMeta, files) + zip.file('meta.json', stringifiedMeta) + + const arrayBuffer = await zip + .generateAsync({ + type: ARRAY_BUFFER, + compression: DEFLATE, + compressionOptions: { + level: 6 + } + }) + .catch((err) => { + console.log('err in zip:>> ', err) + setIsLoading(false) + toast.error(err.message || 'Error occurred in generating zip file') + return null + }) + + if (!arrayBuffer) return Promise.resolve(null) + + return Promise.resolve(arrayBuffer) } return ( @@ -757,7 +748,7 @@ export const VerifyPage = () => { )} currentFile={currentFile} setCurrentFile={setCurrentFile} - handleExport={handleMarkedExport} + handleExport={handleExport} handleEncryptedExport={handleEncryptedExport} /> ) From 245585662a094da385702438bcdd72387cd5267f Mon Sep 17 00:00:00 2001 From: Stixx Date: Tue, 10 Dec 2024 14:46:26 +0100 Subject: [PATCH 09/10] fix: removed redundant variable --- src/pages/sign/index.tsx | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx index 16fa3c4..2c69adf 100644 --- a/src/pages/sign/index.tsx +++ b/src/pages/sign/index.tsx @@ -12,42 +12,40 @@ import { NostrController } from '../../controllers' import { appPrivateRoutes, appPublicRoutes } from '../../routes' import { CreateSignatureEventContent, Meta, SignedEvent } from '../../types' import { + ARRAY_BUFFER, decryptArrayBuffer, + DEFLATE, encryptArrayBuffer, extractMarksFromSignedMeta, extractZipUrlAndEncryptionKey, + filterMarksByPubkey, + findOtherUserMarks, generateEncryptionKey, generateKeysFile, getCurrentUserFiles, + getCurrentUserMarks, getHash, hexToNpub, isOnline, loadZip, - unixNow, npubToHex, parseJson, + processMarks, readContentOfZipEntry, sendNotification, signEventForMetaFile, - updateUsersAppData, - findOtherUserMarks, timeout, - processMarks, - ARRAY_BUFFER + unixNow, + updateMarks, + updateUsersAppData } from '../../utils' import { CurrentUserMark, Mark } from '../../types/mark.ts' -import { - filterMarksByPubkey, - getCurrentUserMarks, - updateMarks -} from '../../utils' import PdfMarking from '../../components/PDFView/PdfMarking.tsx' import { convertToSigitFile, getZipWithFiles, SigitFile } from '../../utils/file.ts' -import { DEFLATE } from '../../utils' import { generateTimestamp } from '../../utils/opentimestamps.ts' import { MARK_TYPE_CONFIG } from '../../components/MarkTypeStrategy/MarkStrategy.tsx' import { getLastSignersSig } from '../../utils/sign.ts' @@ -195,11 +193,10 @@ export const SignPage = () => { m.value && encryptionKey ) { - const decrypted = await fetchAndDecrypt( + otherUserMarks[i].value = await fetchAndDecrypt( m.value, encryptionKey ) - otherUserMarks[i].value = decrypted } } catch (error) { console.error(`Error during mark fetchAndDecrypt phase`, error) From 99fa3add562c4efa013954c0e0df95723b9aea3d Mon Sep 17 00:00:00 2001 From: Stixx Date: Tue, 10 Dec 2024 15:29:24 +0100 Subject: [PATCH 10/10] fix: bug, when valid npub, clicking + was saying npub was invalid --- src/pages/create/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index 1defde7..0ce990f 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -1162,7 +1162,9 @@ export const CreatePage = () => { ) : (