From f4877b55dd9026fa5ea92de082cce3996ae2dc1b Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Wed, 15 May 2024 16:03:31 +0500 Subject: [PATCH 01/16] chore: remove unused code --- src/pages/decrypt/index.tsx | 141 ---------------------------- src/pages/decrypt/style.module.scss | 27 ------ 2 files changed, 168 deletions(-) delete mode 100644 src/pages/decrypt/index.tsx delete mode 100644 src/pages/decrypt/style.module.scss diff --git a/src/pages/decrypt/index.tsx b/src/pages/decrypt/index.tsx deleted file mode 100644 index 6b20491..0000000 --- a/src/pages/decrypt/index.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { Box, Button, TextField, Typography } from '@mui/material' -import saveAs from 'file-saver' -import { MuiFileInput } from 'mui-file-input' -import { useEffect, useState } from 'react' -import { LoadingSpinner } from '../../components/LoadingSpinner' -import { decryptArrayBuffer } from '../../utils' -import styles from './style.module.scss' -import { toast } from 'react-toastify' -import { useSearchParams } from 'react-router-dom' -import axios from 'axios' -import { DecryptionError } from '../../types/errors/DecryptionError' - -export const DecryptZip = () => { - const [searchParams] = useSearchParams() - - const [selectedFile, setSelectedFile] = useState(null) - const [encryptionKey, setEncryptionKey] = useState('') - - const [isLoading, setIsLoading] = useState(false) - const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') - const [isDraggingOver, setIsDraggingOver] = useState(false) - - useEffect(() => { - const fileUrl = searchParams.get('file') - - if (fileUrl) { - setIsLoading(true) - setLoadingSpinnerDesc('Fetching zip file') - axios - .get(fileUrl, { - responseType: 'arraybuffer' - }) - .then((res) => { - const fileName = fileUrl.split('/').pop() - const file = new File([res.data], fileName!) - setSelectedFile(file) - }) - .catch((err) => { - console.error( - `error occurred in getting zip file from ${fileUrl}`, - err - ) - }) - .finally(() => { - setIsLoading(false) - }) - } - - const key = searchParams.get('key') - if (key) setEncryptionKey(key) - }, [searchParams]) - - const handleDecrypt = async () => { - if (!selectedFile || !encryptionKey) return - - setIsLoading(true) - setLoadingSpinnerDesc('Decrypting zip file') - - const encryptedArrayBuffer = await selectedFile.arrayBuffer() - - const arrayBuffer = await decryptArrayBuffer( - encryptedArrayBuffer, - encryptionKey - ).catch((err: DecryptionError) => { - console.log('err in decryption:>> ', err) - - toast.error(err.message) - setIsLoading(false) - return null - }) - - if (!arrayBuffer) return - - const blob = new Blob([arrayBuffer]) - saveAs(blob, 'decrypted.zip') - - setIsLoading(false) - } - - const handleDrop = (event: React.DragEvent) => { - event.preventDefault() - setIsDraggingOver(false) - const file = event.dataTransfer.files[0] - if (file.type === 'application/zip') setSelectedFile(file) - } - - const handleDragOver = (event: React.DragEvent) => { - event.preventDefault() - setIsDraggingOver(true) - } - - return ( - <> - {isLoading && } - - - Select encrypted zip file - - - - {isDraggingOver && ( - - Drop file here - - )} - setSelectedFile(value)} - InputProps={{ - inputProps: { - accept: '.zip' - } - }} - /> - - setEncryptionKey(e.target.value)} - /> - - - - - - - - ) -} diff --git a/src/pages/decrypt/style.module.scss b/src/pages/decrypt/style.module.scss deleted file mode 100644 index ae72e77..0000000 --- a/src/pages/decrypt/style.module.scss +++ /dev/null @@ -1,27 +0,0 @@ -@import '../../colors.scss'; - -.container { - display: flex; - flex-direction: column; - color: $text-color; - - .inputBlock { - position: relative; - display: flex; - flex-direction: column; - gap: 25px; - } - - .fileDragOver { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(255, 255, 255, 0.8); - z-index: 1; - display: flex; - justify-content: center; - align-items: center; - } -} From 75bc984a8b433c88ebd7d21c9853122efba3286b Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Wed, 15 May 2024 16:11:57 +0500 Subject: [PATCH 02/16] chore: convert verify to sign --- src/pages/create/index.tsx | 11 +++-------- src/pages/home/index.tsx | 4 ++-- src/pages/{verify => sign}/index.tsx | 2 +- src/pages/{verify => sign}/style.module.scss | 0 src/routes/index.tsx | 8 ++++---- src/utils/misc.ts | 6 ++++-- 6 files changed, 14 insertions(+), 17 deletions(-) rename src/pages/{verify => sign}/index.tsx (99%) rename src/pages/{verify => sign}/style.module.scss (100%) diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index f04e559..11b1890 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -20,11 +20,11 @@ import { } from '@mui/material' import { MuiFileInput } from 'mui-file-input' import { useEffect, useState } from 'react' -import { Link, useNavigate } from 'react-router-dom' +import { Link } from 'react-router-dom' import placeholderAvatar from '../../assets/images/nostr-logo.jpg' import { LoadingSpinner } from '../../components/LoadingSpinner' import { MetadataController, NostrController } from '../../controllers' -import { appPrivateRoutes, getProfileRoute } from '../../routes' +import { getProfileRoute } from '../../routes' import { ProfileMetadata, User, UserRole } from '../../types' import { encryptArrayBuffer, @@ -45,7 +45,6 @@ import { useSelector } from 'react-redux' import { State } from '../../store/rootReducer' export const CreatePage = () => { - const navigate = useNavigate() const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') @@ -350,11 +349,7 @@ export const CreatePage = () => { } setIsLoading(false) - navigate( - `${appPrivateRoutes.verify}?file=${encodeURIComponent( - fileUrl - )}&key=${encodeURIComponent(encryptionKey)}` - ) + // todo: navigate to verify } if (authUrl) { diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 4c4306d..79b1261 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -15,10 +15,10 @@ export const HomePage = () => { Create ) diff --git a/src/pages/verify/index.tsx b/src/pages/sign/index.tsx similarity index 99% rename from src/pages/verify/index.tsx rename to src/pages/sign/index.tsx index 2eec5a1..97d5d27 100644 --- a/src/pages/verify/index.tsx +++ b/src/pages/sign/index.tsx @@ -50,7 +50,7 @@ enum SignedStatus { User_Is_Not_Next_Signer } -export const VerifyPage = () => { +export const SignPage = () => { const [searchParams] = useSearchParams() const [displayInput, setDisplayInput] = useState(false) diff --git a/src/pages/verify/style.module.scss b/src/pages/sign/style.module.scss similarity index 100% rename from src/pages/verify/style.module.scss rename to src/pages/sign/style.module.scss diff --git a/src/routes/index.tsx b/src/routes/index.tsx index dbbf728..93eb430 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -4,12 +4,12 @@ import { LandingPage } from '../pages/landing/LandingPage' import { Login } from '../pages/login' import { ProfilePage } from '../pages/profile' import { hexToNpub } from '../utils' -import { VerifyPage } from '../pages/verify' +import { SignPage } from '../pages/sign' export const appPrivateRoutes = { homePage: '/', create: '/create', - verify: '/verify' + sign: '/sign' } export const appPublicRoutes = { @@ -49,7 +49,7 @@ export const privateRoutes = [ element: }, { - path: appPrivateRoutes.verify, - element: + path: appPrivateRoutes.sign, + element: } ] diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 6f5922c..3aa2cfc 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -76,7 +76,7 @@ export const sendDM = async ( : 'You have received a signed document.' const decryptionUrl = `${window.location.origin}/#${ - appPrivateRoutes.verify + appPrivateRoutes.sign }?file=${encodeURIComponent(fileUrl)}&key=${encodeURIComponent( encryptionKey )}` @@ -166,7 +166,9 @@ export const sendDM = async ( toast.error('An error occurred while publishing DM') errResults.forEach((errResult: any) => { - toast.error(`Publishing to ${errResult.relay} caused the following error: ${errResult.error}`) + toast.error( + `Publishing to ${errResult.relay} caused the following error: ${errResult.error}` + ) }) return null From 32c9a062c461d86a456be246d8395483482c49bf Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Wed, 15 May 2024 16:25:21 +0500 Subject: [PATCH 03/16] chore: decodeURIComponent of encryption key when entered manually --- src/pages/sign/index.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx index 97d5d27..0f906bf 100644 --- a/src/pages/sign/index.tsx +++ b/src/pages/sign/index.tsx @@ -206,7 +206,10 @@ export const SignPage = () => { if (!selectedFile || !encryptionKey) return setIsLoading(true) - const arrayBuffer = await decrypt(selectedFile, encryptionKey) + const arrayBuffer = await decrypt( + selectedFile, + decodeURIComponent(encryptionKey) + ) if (!arrayBuffer) return @@ -478,11 +481,6 @@ export const SignPage = () => { placeholder='Select file' value={selectedFile} onChange={(value) => setSelectedFile(value)} - InputProps={{ - inputProps: { - accept: '.zip' - } - }} /> {selectedFile && ( From cc9fb50b079ff4f0d67ade1f30973f437f9e4ef6 Mon Sep 17 00:00:00 2001 From: ^ Date: Wed, 15 May 2024 18:48:28 +0100 Subject: [PATCH 04/16] fix: landing page --- src/pages/landing/LandingPage.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/pages/landing/LandingPage.tsx b/src/pages/landing/LandingPage.tsx index 9a24f04..4f076ea 100644 --- a/src/pages/landing/LandingPage.tsx +++ b/src/pages/landing/LandingPage.tsx @@ -59,7 +59,7 @@ export const LandingPage = () => { }} variant="h4" > - What is Nostr? + What is SIGit? { }} variant="body1" > - Nostr is a decentralised messaging protocol where YOU own your - identity. To get started, you must have an existing{' '} + SIGit is an open-source and self-hostable solution for secure + document signing and verification. Code is MIT licenced and + available at - Nostr account + git.sigit.io/sig/it .

- No email required - all notifications are made using the nQuiz - relay. + Multi-device - Create, Sign and Verify signature packs from any + device with a browser.

- If you no longer wish to hear from us, simply remove - relay.nquiz.io from your list of relays. + Secure and Private - Documents encrypted at each step, content can + only be decrypted by named recipients.
From 4dd6b6d7a449a0f41db995fb577c42124b234137 Mon Sep 17 00:00:00 2001 From: ^ Date: Wed, 15 May 2024 19:28:27 +0100 Subject: [PATCH 05/16] fix: landing page wording --- src/pages/landing/LandingPage.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pages/landing/LandingPage.tsx b/src/pages/landing/LandingPage.tsx index 4f076ea..948ab72 100644 --- a/src/pages/landing/LandingPage.tsx +++ b/src/pages/landing/LandingPage.tsx @@ -71,23 +71,24 @@ export const LandingPage = () => { > SIGit is an open-source and self-hostable solution for secure document signing and verification. Code is MIT licenced and - available at + available at{' '} - git.sigit.io/sig/it + https://git.sigit.io/sig/it .

- Multi-device - Create, Sign and Verify signature packs from any + SIGit lets you Create, Sign and Verify signature packs from any device with a browser.

- Secure and Private - Documents encrypted at each step, content can - only be decrypted by named recipients. + Unlike other solutions, SIGit is totally private - files are + encrypted locally, and valid packs can only be exported by named + recipients. From 047e753e72139bd84ed44ce6c3c8238ae0f8aa35 Mon Sep 17 00:00:00 2001 From: ^ Date: Wed, 15 May 2024 23:44:21 +0100 Subject: [PATCH 06/16] chore: input box label --- src/pages/login/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 7d26348..869efb2 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -303,7 +303,7 @@ export const Login = () => {
Welcome to Sigit setInputValue(e.target.value)} sx={{ width: '100%', mt: 2 }} From 79b78bd1864c8f45203b48be3efd6534053f8177 Mon Sep 17 00:00:00 2001 From: ^ Date: Wed, 15 May 2024 23:46:10 +0100 Subject: [PATCH 07/16] chore: update relay list --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 6b61434..7239454 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -VITE_MOST_POPULAR_RELAYS=wss://relay.damus.io wss://eden.nostr.land wss://nos.lol wss://relay.snort.social wss://relay.current.fyi wss://brb.io wss://nostr.orangepill.dev wss://nostr-pub.wellorder.net wss://nostr.bitcoiner.social wss://nostr.wine wss://nostr.oxtr.dev wss://relay.nostr.bg wss://nostr.mom wss://nostr.fmt.wiz.biz wss://relay.nostr.band wss://nostr-pub.semisol.dev wss://nostr.milou.lol wss://puravida.nostr.land wss://nostr.onsats.org wss://relay.nostr.info wss://offchain.pub wss://relay.orangepill.dev wss://no.str.cr wss://atlas.nostr.land wss://nostr.zebedee.cloud wss://nostr-relay.wlvs.space wss://relay.nostrati.com wss://relay.nostr.com.au wss://nostr.inosta.cc wss://nostr.rocks \ No newline at end of file +VITE_MOST_POPULAR_RELAYS=wss://relay.damus.io wss://eden.nostr.land wss://nos.lol wss://relay.snort.social wss://relay.current.fyi wss://brb.io wss://nostr.orangepill.dev wss://nostr-pub.wellorder.net wss://nostr.wine wss://nostr.oxtr.dev wss://relay.nostr.bg wss://nostr.mom wss://nostr.fmt.wiz.biz wss://relay.nostr.band wss://nostr-pub.semisol.dev wss://nostr.milou.lol wss://puravida.nostr.land wss://nostr.onsats.org wss://relay.nostr.info wss://offchain.pub wss://relay.orangepill.dev wss://no.str.cr wss://atlas.nostr.land wss://nostr.zebedee.cloud wss://nostr-relay.wlvs.space wss://relay.nostrati.com wss://relay.nostr.com.au wss://nostr.inosta.cc wss://nostr.rocks \ No newline at end of file From 0163d51155ffff17b41d9e60871143b6be68e7d6 Mon Sep 17 00:00:00 2001 From: ^ Date: Wed, 15 May 2024 23:54:20 +0100 Subject: [PATCH 08/16] fix: label --- src/pages/login/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 869efb2..d05edb8 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -303,7 +303,7 @@ export const Login = () => {
Welcome to Sigit setInputValue(e.target.value)} sx={{ width: '100%', mt: 2 }} From 05c3f49a17bc78b6b57a59a9a6dc475ab57d5034 Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Thu, 16 May 2024 10:38:27 +0500 Subject: [PATCH 09/16] fix: In sign page, when doc is fully signed, update search params with update file url and key --- src/pages/sign/index.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx index 0f906bf..cbbe6f8 100644 --- a/src/pages/sign/index.tsx +++ b/src/pages/sign/index.tsx @@ -51,7 +51,7 @@ enum SignedStatus { } export const SignPage = () => { - const [searchParams] = useSearchParams() + const [searchParams, setSearchParams] = useSearchParams() const [displayInput, setDisplayInput] = useState(false) @@ -123,7 +123,7 @@ export const SignPage = () => { const fileName = fileUrl.split('/').pop() const file = new File([res.data], fileName!) - decrypt(file, key).then((arrayBuffer) => { + decrypt(file, decodeURIComponent(key)).then((arrayBuffer) => { if (arrayBuffer) handleDecryptedArrayBuffer(arrayBuffer) }) }) @@ -376,6 +376,14 @@ export const SignPage = () => { setAuthUrl ) } + + // when user is the last signer and has sent + // the final document to all the signers and viewers + // update search params with updated file url and encryption key + setSearchParams({ + file: fileUrl, + key: encryptionKey + }) } else { const nextSigner = meta.signers[signerIndex + 1] await sendDM( From 5c1440244cbd3e6d9b80e557dc1a511c1a067871 Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Thu, 16 May 2024 10:40:56 +0500 Subject: [PATCH 10/16] feat: add verify page --- src/pages/home/index.tsx | 6 + src/pages/verify/index.tsx | 332 +++++++++++++++++++++++++++++ src/pages/verify/style.module.scss | 39 ++++ src/routes/index.tsx | 8 +- src/types/core.ts | 1 + 5 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 src/pages/verify/index.tsx create mode 100644 src/pages/verify/style.module.scss diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 79b1261..4f9d477 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -20,6 +20,12 @@ export const HomePage = () => { > Sign + ) } diff --git a/src/pages/verify/index.tsx b/src/pages/verify/index.tsx new file mode 100644 index 0000000..d808294 --- /dev/null +++ b/src/pages/verify/index.tsx @@ -0,0 +1,332 @@ +import { + Box, + Button, + List, + ListItem, + ListSubheader, + Typography, + useTheme +} from '@mui/material' +import JSZip from 'jszip' +import { MuiFileInput } from 'mui-file-input' +import { useEffect, useState } from 'react' +import { Link } from 'react-router-dom' +import { toast } from 'react-toastify' +import placeholderAvatar from '../../assets/images/nostr-logo.jpg' +import { LoadingSpinner } from '../../components/LoadingSpinner' +import { MetadataController } from '../../controllers' +import { getProfileRoute } from '../../routes' +import { Meta, ProfileMetadata } from '../../types' +import { + hexToNpub, + parseJson, + readContentOfZipEntry, + shorten +} from '../../utils' +import styles from './style.module.scss' +import { Event, verifyEvent } from 'nostr-tools' + +export const VerifyPage = () => { + const theme = useTheme() + + const textColor = theme.palette.getContrastText( + theme.palette.background.paper + ) + + const [isLoading, setIsLoading] = useState(false) + const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') + + const [selectedFile, setSelectedFile] = useState(null) + const [meta, setMeta] = useState(null) + + const [metadata, setMetadata] = useState<{ [key: string]: ProfileMetadata }>( + {} + ) + + useEffect(() => { + if (meta) { + const metadataController = new MetadataController() + + const users = [meta.submittedBy, ...meta.signers, ...meta.viewers] + + users.forEach((user) => { + if (!(user in metadata)) { + metadataController + .findMetadata(user) + .then((metadataEvent) => { + const metadataContent = + metadataController.extractProfileMetadataContent(metadataEvent) + if (metadataContent) + setMetadata((prev) => ({ + ...prev, + [user]: metadataContent + })) + }) + .catch((err) => { + console.error( + `error occurred in finding metadata for: ${user}`, + err + ) + }) + } + }) + } + }, [meta]) + + const handleVerify = async () => { + if (!selectedFile) return + setIsLoading(true) + + const zip = await JSZip.loadAsync(selectedFile).catch((err) => { + console.log('err in loading zip file :>> ', err) + toast.error(err.message || 'An error occurred in loading zip file.') + return null + }) + + if (!zip) return + + setLoadingSpinnerDesc('Parsing meta.json') + + const metaFileContent = await readContentOfZipEntry( + zip, + 'meta.json', + 'string' + ) + + if (!metaFileContent) { + setIsLoading(false) + return + } + + const parsedMetaJson = await parseJson(metaFileContent).catch( + (err) => { + console.log('err in parsing the content of meta.json :>> ', err) + toast.error( + err.message || 'error occurred in parsing the content of meta.json' + ) + setIsLoading(false) + return null + } + ) + + setMeta(parsedMetaJson) + setIsLoading(false) + } + + const imageLoadError = (event: any) => { + event.target.src = placeholderAvatar + } + + const getRoboImageUrl = (pubkey: string) => { + const npub = hexToNpub(pubkey) + return `https://robohash.org/${npub}.png?set=set3` + } + + const displayUser = (pubkey: string, verifySignature = false) => { + const profile = metadata[pubkey] + + let isValidSignature = false + + if (verifySignature) { + const signedEventString = meta ? meta.signedEvents[pubkey] : null + if (signedEventString) { + try { + const signedEvent = JSON.parse(signedEventString) + isValidSignature = verifyEvent(signedEvent) + } catch (error) { + console.error( + `An error occurred in parsing and verifying the signature event for ${pubkey}`, + error + ) + } + } + } + + return ( + + Profile Image + + + {profile?.display_name || + profile?.name || + shorten(hexToNpub(pubkey))} + + + {verifySignature && ( + + ({isValidSignature ? 'Valid' : 'Not Valid'} Signature) + + )} + + ) + } + + const displayExportedBy = () => { + if (!meta || !meta.exportSignature) return null + + const exportSignatureString = meta.exportSignature + + try { + const exportSignatureEvent = JSON.parse(exportSignatureString) as Event + + if (verifyEvent(exportSignatureEvent)) { + return displayUser(exportSignatureEvent.pubkey) + } else { + toast.error(`Invalid export signature!`) + return ( + + Invalid export signature + + ) + } + } catch (error) { + console.error(`An error occurred wile parsing exportSignature`, error) + return null + } + } + + return ( + <> + {isLoading && } + + {!meta && ( + <> + + Select exported zip file + + + setSelectedFile(value)} + InputProps={{ + inputProps: { + accept: '.zip' + } + }} + /> + + {selectedFile && ( + + + + )} + + )} + + {meta && ( + <> + + Meta Info + + } + > + + + Submitted By + + {displayUser(meta.submittedBy)} + + + + + Exported By + + {displayExportedBy()} + + + {meta.signers.length > 0 && ( + + + Signers + +
    + {meta.signers.map((signer) => ( +
  • + {displayUser(signer, true)} +
  • + ))} +
+
+ )} + + {meta.viewers.length > 0 && ( + + + Viewers + +
    + {meta.viewers.map((viewer) => ( +
  • + {displayUser(viewer)} +
  • + ))} +
+
+ )} + + + + Files + +
    + {Object.keys(meta.fileHashes).map((file, index) => ( +
  • + {file} +
  • + ))} +
+
+
+ + )} +
+ + ) +} diff --git a/src/pages/verify/style.module.scss b/src/pages/verify/style.module.scss new file mode 100644 index 0000000..61fe63d --- /dev/null +++ b/src/pages/verify/style.module.scss @@ -0,0 +1,39 @@ +@import '../../colors.scss'; + +.container { + color: $text-color; + display: flex; + flex-direction: column; + + .subHeader { + border-bottom: 0.5px solid; + font-size: 1.5rem; + } + + .usersList { + display: flex; + flex-direction: column; + gap: 10px; + list-style: none; + margin-top: 10px; + } + + .user { + display: flex; + align-items: center; + gap: 10px; + + .name { + text-align: center; + cursor: pointer; + } + } + + .tableCell { + border-right: 1px solid rgba(224, 224, 224, 1); + + .user { + @extend .user; + } + } +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 93eb430..1796e19 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -5,11 +5,13 @@ import { Login } from '../pages/login' import { ProfilePage } from '../pages/profile' import { hexToNpub } from '../utils' import { SignPage } from '../pages/sign' +import { VerifyPage } from '../pages/verify' export const appPrivateRoutes = { homePage: '/', create: '/create', - sign: '/sign' + sign: '/sign', + verify: '/verify' } export const appPublicRoutes = { @@ -51,5 +53,9 @@ export const privateRoutes = [ { path: appPrivateRoutes.sign, element: + }, + { + path: appPrivateRoutes.verify, + element: } ] diff --git a/src/types/core.ts b/src/types/core.ts index 531e1fd..a328ac0 100644 --- a/src/types/core.ts +++ b/src/types/core.ts @@ -14,4 +14,5 @@ export interface Meta { fileHashes: { [key: string]: string } submittedBy: string signedEvents: { [key: string]: string } + exportSignature?: string } From 8f463b36c08761a4a8e4ff9b67068be90eef8ea6 Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Thu, 16 May 2024 10:43:37 +0500 Subject: [PATCH 11/16] feat: In sign page navigate to verify after export --- src/pages/sign/index.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx index cbbe6f8..0d21b51 100644 --- a/src/pages/sign/index.tsx +++ b/src/pages/sign/index.tsx @@ -21,12 +21,12 @@ import { MuiFileInput } from 'mui-file-input' import { EventTemplate } from 'nostr-tools' import { useEffect, useState } from 'react' import { useSelector } from 'react-redux' -import { Link, useSearchParams } from 'react-router-dom' +import { Link, useNavigate, useSearchParams } from 'react-router-dom' import { toast } from 'react-toastify' import placeholderAvatar from '../../assets/images/nostr-logo.jpg' import { LoadingSpinner } from '../../components/LoadingSpinner' import { MetadataController, NostrController } from '../../controllers' -import { getProfileRoute } from '../../routes' +import { appPrivateRoutes, getProfileRoute } from '../../routes' import { State } from '../../store/rootReducer' import { Meta, ProfileMetadata, User, UserRole } from '../../types' import { @@ -51,6 +51,7 @@ enum SignedStatus { } export const SignPage = () => { + const navigate = useNavigate() const [searchParams, setSearchParams] = useSearchParams() const [displayInput, setDisplayInput] = useState(false) @@ -461,6 +462,8 @@ export const SignPage = () => { saveAs(blob, 'exported.zip') setIsLoading(false) + + navigate(appPrivateRoutes.verify) } if (authUrl) { From 00db735106767ccd3a934c9d4f9124b40ef2b7b3 Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Thu, 16 May 2024 10:51:35 +0500 Subject: [PATCH 12/16] fix: handle navigation after create --- src/pages/create/index.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index 11b1890..25a29a7 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -20,11 +20,11 @@ import { } from '@mui/material' import { MuiFileInput } from 'mui-file-input' import { useEffect, useState } from 'react' -import { Link } from 'react-router-dom' +import { Link, useNavigate } from 'react-router-dom' import placeholderAvatar from '../../assets/images/nostr-logo.jpg' import { LoadingSpinner } from '../../components/LoadingSpinner' import { MetadataController, NostrController } from '../../controllers' -import { getProfileRoute } from '../../routes' +import { appPrivateRoutes, getProfileRoute } from '../../routes' import { ProfileMetadata, User, UserRole } from '../../types' import { encryptArrayBuffer, @@ -45,6 +45,7 @@ import { useSelector } from 'react-redux' import { State } from '../../store/rootReducer' export const CreatePage = () => { + const navigate = useNavigate() const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') @@ -349,7 +350,7 @@ export const CreatePage = () => { } setIsLoading(false) - // todo: navigate to verify + navigate(`${appPrivateRoutes.sign}?file=${fileUrl}&key=${encryptionKey}`) } if (authUrl) { From e4675af4dd8267a88bed50bf3d3f1aa323d17987 Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Thu, 16 May 2024 10:55:44 +0500 Subject: [PATCH 13/16] fix: handle the case when zip entry is undefined --- src/utils/zip.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/utils/zip.ts b/src/utils/zip.ts index 1d6379a..71bc556 100644 --- a/src/utils/zip.ts +++ b/src/utils/zip.ts @@ -17,6 +17,11 @@ export const readContentOfZipEntry = async ( // Get the zip entry corresponding to the specified file path const zipEntry = zip.files[filePath] + if (!zipEntry) { + toast.error(`Couldn't find file in zip archive at ${filePath}`) + return null + } + // Read the content of the zip entry asynchronously const fileContent = await zipEntry.async(outputType).catch((err) => { // Handle any errors that occur during the read operation From 1867ba6a14a3605c85664fbb216e2b069a8cb40c Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Thu, 16 May 2024 11:04:19 +0500 Subject: [PATCH 14/16] chore: quick fix --- src/pages/create/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index 25a29a7..21732c0 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -350,7 +350,11 @@ export const CreatePage = () => { } setIsLoading(false) - navigate(`${appPrivateRoutes.sign}?file=${fileUrl}&key=${encryptionKey}`) + navigate( + `${appPrivateRoutes.sign}?file=${encodeURIComponent( + fileUrl + )}&key=${encodeURIComponent(encryptionKey)}` + ) } if (authUrl) { From ad9c88a4f56dd9222ace3a41380a2ddc7d3cc24b Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Thu, 16 May 2024 11:23:26 +0500 Subject: [PATCH 15/16] chore: merge main --- .env.example | 2 +- .prettierrc | 7 ++++++ package.json | 2 ++ src/App.tsx | 2 +- src/components/AppBar/AppBar.tsx | 12 ++++----- src/components/username.tsx | 14 +++++------ src/controllers/MetadataController.ts | 10 +++----- src/controllers/NostrController.ts | 10 +++----- src/layouts/Main.tsx | 4 +-- src/pages/create/index.tsx | 36 +++++++++++++-------------- src/pages/home/index.tsx | 4 +-- src/pages/landing/LandingPage.tsx | 30 +++++++++++----------- src/pages/login/index.tsx | 19 +++++++------- src/pages/profile/index.tsx | 31 +++++++++++++---------- src/types/errors/DecryptionError.ts | 13 +++++----- src/utils/crypto.ts | 4 +-- src/utils/misc.ts | 8 +++--- 17 files changed, 110 insertions(+), 98 deletions(-) create mode 100644 .prettierrc diff --git a/.env.example b/.env.example index 6b61434..7239454 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -VITE_MOST_POPULAR_RELAYS=wss://relay.damus.io wss://eden.nostr.land wss://nos.lol wss://relay.snort.social wss://relay.current.fyi wss://brb.io wss://nostr.orangepill.dev wss://nostr-pub.wellorder.net wss://nostr.bitcoiner.social wss://nostr.wine wss://nostr.oxtr.dev wss://relay.nostr.bg wss://nostr.mom wss://nostr.fmt.wiz.biz wss://relay.nostr.band wss://nostr-pub.semisol.dev wss://nostr.milou.lol wss://puravida.nostr.land wss://nostr.onsats.org wss://relay.nostr.info wss://offchain.pub wss://relay.orangepill.dev wss://no.str.cr wss://atlas.nostr.land wss://nostr.zebedee.cloud wss://nostr-relay.wlvs.space wss://relay.nostrati.com wss://relay.nostr.com.au wss://nostr.inosta.cc wss://nostr.rocks \ No newline at end of file +VITE_MOST_POPULAR_RELAYS=wss://relay.damus.io wss://eden.nostr.land wss://nos.lol wss://relay.snort.social wss://relay.current.fyi wss://brb.io wss://nostr.orangepill.dev wss://nostr-pub.wellorder.net wss://nostr.wine wss://nostr.oxtr.dev wss://relay.nostr.bg wss://nostr.mom wss://nostr.fmt.wiz.biz wss://relay.nostr.band wss://nostr-pub.semisol.dev wss://nostr.milou.lol wss://puravida.nostr.land wss://nostr.onsats.org wss://relay.nostr.info wss://offchain.pub wss://relay.orangepill.dev wss://no.str.cr wss://atlas.nostr.land wss://nostr.zebedee.cloud wss://nostr-relay.wlvs.space wss://relay.nostrati.com wss://relay.nostr.com.au wss://nostr.inosta.cc wss://nostr.rocks \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..5b429c3 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "trailingComma": "none", + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "endOfLine": "auto" +} diff --git a/package.json b/package.json index 6d1ea04..75a6c85 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint:fix": "eslint . --fix --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "formatter:check": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"", + "formatter:fix": "npx prettier --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"", "preview": "vite preview" }, "dependencies": { diff --git a/src/App.tsx b/src/App.tsx index 6b2a50d..58f0c71 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -67,7 +67,7 @@ const App = () => { })} { const isAuthenticated = authState?.loggedIn === true return ( - + - Logo navigate('/')} /> + Logo navigate('/')} /> @@ -99,7 +99,7 @@ export const AppBar = () => { onClick={() => { navigate(appPublicRoutes.login) }} - variant='contained' + variant="contained" > Sign in @@ -113,7 +113,7 @@ export const AppBar = () => { handleClick={handleOpenUserMenu} /> { display: { md: 'none' } }} > - {username} + {username} { { return ( user-avatar { }} /> { if (res.status === 'rejected') { - failedPublishes.push( - { - relay: relays[index], - error: res.reason.message - } - ) + failedPublishes.push({ + relay: relays[index], + error: res.reason.message + }) } }) diff --git a/src/layouts/Main.tsx b/src/layouts/Main.tsx index 8d1bd81..b452354 100644 --- a/src/layouts/Main.tsx +++ b/src/layouts/Main.tsx @@ -59,13 +59,13 @@ export const MainLayout = () => { setIsLoading(false) }, [dispatch]) - if (isLoading) return + if (isLoading) return return ( <> - + { if (authUrl) { return (