From 1f9954befd01c4e9f90330f3db89aa75f0039bbe Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Tue, 14 May 2024 14:27:05 +0500 Subject: [PATCH] feat: update signing flow --- src/components/AppBar/AppBar.tsx | 140 +---- src/pages/home/index.tsx | 495 +---------------- src/pages/home/style.module.scss | 31 +- src/pages/sign/index.tsx | 596 +++++++++++++++------ src/pages/sign/style.module.scss | 34 +- src/pages/verify/index.tsx | 817 +++++++++++++++++++++++++++++ src/pages/verify/style.module.scss | 18 + src/routes/index.tsx | 16 +- src/types/core.ts | 18 + src/types/index.ts | 1 + src/utils/misc.ts | 12 +- src/utils/string.ts | 4 +- 12 files changed, 1336 insertions(+), 846 deletions(-) create mode 100644 src/pages/verify/index.tsx create mode 100644 src/pages/verify/style.module.scss create mode 100644 src/types/core.ts diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index c916977..3f1c858 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -1,13 +1,9 @@ -import { Menu as MenuIcon } from '@mui/icons-material' import { AppBar as AppBarMui, Box, Button, - IconButton, Menu, MenuItem, - Tab, - Tabs, Toolbar, Typography } from '@mui/material' @@ -19,14 +15,10 @@ import { State } from '../../store/rootReducer' import { Dispatch } from '../../store/store' import Username from '../username' -import { Link, useLocation, useNavigate } from 'react-router-dom' +import { Link, useNavigate } from 'react-router-dom' import nostrichAvatar from '../../assets/images/avatar.png' import { NostrController } from '../../controllers' -import { - appPrivateRoutes, - appPublicRoutes, - getProfileRoute -} from '../../routes' +import { appPublicRoutes, getProfileRoute } from '../../routes' import { clearAuthToken, saveNsecBunkerDelegatedKey, @@ -34,20 +26,14 @@ import { } from '../../utils' import styles from './style.module.scss' -const validTabs = [appPrivateRoutes.homePage, appPrivateRoutes.decryptZip] - export const AppBar = () => { const navigate = useNavigate() - const { pathname } = useLocation() - const [tabValue, setTabValue] = useState( - validTabs.includes(pathname) ? pathname : '/' - ) + const dispatch: Dispatch = useDispatch() const [username, setUsername] = useState('') const [userAvatar, setUserAvatar] = useState(nostrichAvatar) const [anchorElUser, setAnchorElUser] = useState(null) - const [anchorElNav, setAnchorElNav] = useState(null) const authState = useSelector((state: State) => state.auth) const metadataState = useSelector((state: State) => state.metadata) @@ -66,18 +52,10 @@ export const AppBar = () => { setAnchorElUser(event.currentTarget) } - const handleOpenNavMenu = (event: React.MouseEvent) => { - setAnchorElNav(event.currentTarget) - } - const handleCloseUserMenu = () => { setAnchorElUser(null) } - const handleCloseNavMenu = () => { - setAnchorElNav(null) - } - const handleProfile = () => { const hexKey = authState?.usersPubkey if (hexKey) navigate(getProfileRoute(hexKey)) @@ -111,116 +89,8 @@ export const AppBar = () => { return ( - - - Logo navigate('/')} /> - - - {isAuthenticated && ( - setTabValue(value)} - > - - - - - )} - - - - {!isAuthenticated && ( - - Logo navigate('/')} - /> - - )} - - {isAuthenticated && ( - <> - - - - - - - - - - - - - - - - - - - )} + + Logo navigate('/')} /> diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 69ab3f9..438d9c4 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -1,489 +1,20 @@ -import { Clear } from '@mui/icons-material' -import { - Box, - Button, - FormControl, - IconButton, - InputLabel, - List, - ListItem, - ListSubheader, - MenuItem, - Select, - TextField, - Typography -} from '@mui/material' -import JSZip from 'jszip' -import { MuiFileInput } from 'mui-file-input' -import { useEffect, useState } from 'react' -import { useSelector } from 'react-redux' -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, NostrController } from '../../controllers' -import { getProfileRoute } from '../../routes' -import { State } from '../../store/rootReducer' -import { ProfileMetadata } from '../../types' -import { - encryptArrayBuffer, - generateEncryptionKey, - getHash, - pubToHex, - queryNip05, - sendDM, - shorten, - signEventForMetaFile, - uploadToFileStorage -} from '../../utils' +import { Box, Button } from '@mui/material' import styles from './style.module.scss' - -enum SelectionType { - signer = 'Signer', - viewer = 'Viewer' -} +import { useNavigate } from 'react-router-dom' +import { appPrivateRoutes } from '../../routes' export const HomePage = () => { - const [inputValue, setInputValue] = useState('') - const [type, setType] = useState(SelectionType.signer) - const [error, setError] = useState() - - const [signers, setSigners] = useState([]) - const [viewers, setViewers] = useState([]) - - const [selectedFiles, setSelectedFiles] = useState([]) - - const [isLoading, setIsLoading] = useState(false) - const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') - const [authUrl, setAuthUrl] = useState() - - const usersPubkey = useSelector((state: State) => state.auth.usersPubkey) - - const nostrController = NostrController.getInstance() - - const handleAddClick = async () => { - setError(undefined) - - const addPubkey = (pubkey: string) => { - const addElement = (prev: string[]) => { - // if key is already in the list just return that - if (prev.includes(pubkey)) return prev - - return [...prev, pubkey] - } - if (type === SelectionType.signer) { - setSigners(addElement) - } else { - setViewers(addElement) - } - } - - if (inputValue.startsWith('npub')) { - const pubkey = await pubToHex(inputValue) - if (pubkey) { - addPubkey(pubkey) - setInputValue('') - } else { - setError('Provided npub is not valid. Please enter correct npub.') - } - return - } - - if (inputValue.includes('@')) { - setIsLoading(true) - setLoadingSpinnerDesc('Querying for nip05') - const nip05Profile = await queryNip05(inputValue) - .catch((err) => { - console.error(`error occurred in querying nip05: ${inputValue}`, err) - return null - }) - .finally(() => { - setIsLoading(false) - setLoadingSpinnerDesc('') - }) - - if (nip05Profile) { - const pubkey = nip05Profile.pubkey - addPubkey(pubkey) - setInputValue('') - } else { - setError('Provided nip05 is not valid. Please enter correct nip05.') - } - return - } - - setError('Invalid input! Make sure to provide correct npub or nip05.') - } - - const handleRemove = (pubkey: string, selectionType: SelectionType) => { - if (selectionType === SelectionType.signer) { - setSigners((prev) => prev.filter((signer) => signer !== pubkey)) - } else { - setViewers((prev) => prev.filter((viewer) => viewer !== pubkey)) - } - } - - const handleSelectFiles = (files: File[]) => { - setSelectedFiles((prev) => { - const prevFileNames = prev.map((file) => file.name) - - const newFiles = files.filter( - (file) => !prevFileNames.includes(file.name) - ) - - return [...prev, ...newFiles] - }) - } - - const handleRemoveFile = (fileToRemove: File) => { - setSelectedFiles((prevFiles) => - prevFiles.filter((file) => file.name !== fileToRemove.name) - ) - } - - const handleSubmit = async () => { - if (signers.length === 0) { - toast.error('No signer is provided. At least provide one signer.') - return - } - - if (viewers.length === 0) { - toast.error('No viewer is provided. At least provide one viewer.') - return - } - - if (selectedFiles.length === 0) { - toast.error('No file is provided. At least provide one file.') - return - } - - setIsLoading(true) - setLoadingSpinnerDesc('Generating hashes for files') - - const fileHashes: { [key: string]: string } = {} - - for (const file of selectedFiles) { - const arraybuffer = await file.arrayBuffer().catch((err) => { - console.log( - `err while getting arrayBuffer of file ${file.name} :>> `, - err - ) - toast.error( - err.message || `err while getting arrayBuffer of file ${file.name}` - ) - return null - }) - - if (!arraybuffer) return - - const hash = await getHash(arraybuffer) - - if (!hash) { - setIsLoading(false) - return - } - - fileHashes[file.name] = hash - } - - const zip = new JSZip() - - selectedFiles.forEach((file) => { - zip.file(`files/${file.name}`, file) - }) - - setLoadingSpinnerDesc('Signing nostr event') - const signedEvent = await signEventForMetaFile( - signers[0], - fileHashes, - nostrController, - setIsLoading - ) - - if (!signedEvent) return - - const meta = { - signers, - viewers, - fileHashes, - submittedBy: usersPubkey, - signedEvents: { - [signedEvent.pubkey]: JSON.stringify(signedEvent, null, 2) - } - } - - try { - const stringifiedMeta = JSON.stringify(meta, null, 2) - zip.file('meta.json', stringifiedMeta) - - const metaHash = await getHash(stringifiedMeta) - if (!metaHash) return - - const metaHashJson = { - [usersPubkey!]: metaHash - } - - zip.file('hashes.json', JSON.stringify(metaHashJson, null, 2)) - } catch (err) { - toast.error('An error occurred in converting meta json to string') - } - - setLoadingSpinnerDesc('Generating zip file') - - 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 encryptionKey = await generateEncryptionKey() - - setLoadingSpinnerDesc('Encrypting zip file') - const encryptedArrayBuffer = await encryptArrayBuffer( - arraybuffer, - encryptionKey - ) - - const blob = new Blob([encryptedArrayBuffer]) - - setLoadingSpinnerDesc('Uploading zip file to file storage.') - const fileUrl = await uploadToFileStorage(blob, nostrController) - .then((url) => { - toast.success('zip file uploaded to file storage') - return url - }) - .catch((err) => { - console.log('err in upload:>> ', err) - setIsLoading(false) - toast.error(err.message || 'Error occurred in uploading zip file') - return null - }) - - if (!fileUrl) return - - setLoadingSpinnerDesc('Sending DM to first signer') - await sendDM( - fileUrl, - encryptionKey, - signers[0], - nostrController, - true, - setAuthUrl - ) - - setIsLoading(false) - } - - if (authUrl) { - return ( -