From a3abf40f44e54e5aed199db7f34a7b6d40b2c2bb Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Wed, 12 Jun 2024 15:10:34 +0500 Subject: [PATCH] chore(refactor): move DisplayMeta component to separate file --- src/pages/sign/index.tsx | 435 +----------------------- src/pages/sign/internal/displayMeta.tsx | 426 +++++++++++++++++++++++ 2 files changed, 437 insertions(+), 424 deletions(-) create mode 100644 src/pages/sign/internal/displayMeta.tsx diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx index 715bdcc..d0d218d 100644 --- a/src/pages/sign/index.tsx +++ b/src/pages/sign/index.tsx @@ -1,67 +1,37 @@ -import { - Box, - Button, - IconButton, - List, - ListItem, - ListSubheader, - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Tooltip, - Typography, - useTheme -} from '@mui/material' +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, kinds, verifyEvent } from 'nostr-tools' +import { Event, verifyEvent } from 'nostr-tools' import { useEffect, useState } from 'react' import { useSelector } from 'react-redux' -import { useNavigate, useSearchParams, useLocation } from 'react-router-dom' +import { useLocation, useNavigate, useSearchParams } from 'react-router-dom' import { toast } from 'react-toastify' import { LoadingSpinner } from '../../components/LoadingSpinner' -import { UserComponent } from '../../components/username' -import { MetadataController, NostrController } from '../../controllers' +import CopyModal from '../../components/copyModal' +import { NostrController } from '../../controllers' import { appPublicRoutes } from '../../routes' import { State } from '../../store/rootReducer' -import { - CreateSignatureEventContent, - Meta, - ProfileMetadata, - SignedEvent, - SignedEventContent, - User, - UserRole -} from '../../types' +import { CreateSignatureEventContent, Meta, SignedEvent } from '../../types' import { decryptArrayBuffer, encryptArrayBuffer, generateEncryptionKey, + generateKeysFile, getHash, hexToNpub, - parseJson, + isOnline, npubToHex, + parseJson, readContentOfZipEntry, sendDM, - shorten, signEventForMetaFile, - uploadToFileStorage, - isOnline, - generateKeysFile + uploadToFileStorage } from '../../utils' +import { DisplayMeta } from './internal/displayMeta' import styles from './style.module.scss' -import { - Cancel, - CheckCircle, - Download, - HourglassTop -} from '@mui/icons-material' -import CopyModal from '../../components/copyModal' enum SignedStatus { Fully_Signed, User_Is_Next_Signer, @@ -967,386 +937,3 @@ export const SignPage = () => { ) } - -type DisplayMetaProps = { - meta: Meta - zip: JSZip - submittedBy: string - signers: `npub1${string}`[] - viewers: `npub1${string}`[] - creatorFileHashes: { [key: string]: string } - currentFileHashes: { [key: string]: string | null } - signedBy: `npub1${string}`[] - nextSigner?: string - getPrevSignersSig: (usersNpub: string) => string | null -} - -const DisplayMeta = ({ - meta, - zip, - submittedBy, - signers, - viewers, - creatorFileHashes, - currentFileHashes, - signedBy, - nextSigner, - getPrevSignersSig -}: DisplayMetaProps) => { - const theme = useTheme() - - const textColor = theme.palette.getContrastText( - theme.palette.background.paper - ) - - const [metadata, setMetadata] = useState<{ [key: string]: ProfileMetadata }>( - {} - ) - const [users, setUsers] = useState([]) - - useEffect(() => { - signers.forEach((signer) => { - const hexKey = npubToHex(signer) - setUsers((prev) => { - if (prev.findIndex((user) => user.pubkey === hexKey) !== -1) return prev - - return [ - ...prev, - { - pubkey: hexKey!, - role: UserRole.signer - } - ] - }) - }) - - viewers.forEach((viewer) => { - const hexKey = npubToHex(viewer) - setUsers((prev) => { - if (prev.findIndex((user) => user.pubkey === hexKey) !== -1) return prev - - return [ - ...prev, - { - pubkey: hexKey!, - role: UserRole.viewer - } - ] - }) - }) - }, [signers, viewers]) - - useEffect(() => { - const metadataController = new MetadataController() - - const hexKeys: string[] = [ - npubToHex(submittedBy)!, - ...users.map((user) => user.pubkey) - ] - - hexKeys.forEach((key) => { - if (!(key in metadata)) { - const handleMetadataEvent = (event: Event) => { - const metadataContent = - metadataController.extractProfileMetadataContent(event) - - if (metadataContent) - setMetadata((prev) => ({ - ...prev, - [key]: metadataContent - })) - } - - metadataController.on(key, (kind: number, event: Event) => { - if (kind === kinds.Metadata) { - handleMetadataEvent(event) - } - }) - - metadataController - .findMetadata(key) - .then((metadataEvent) => { - if (metadataEvent) handleMetadataEvent(metadataEvent) - }) - .catch((err) => { - console.error(`error occurred in finding metadata for: ${key}`, err) - }) - } - }) - }, [users, submittedBy]) - - const downloadFile = async (filename: string) => { - const arrayBuffer = await readContentOfZipEntry( - zip, - `files/${filename}`, - 'arraybuffer' - ) - if (!arrayBuffer) return - - const blob = new Blob([arrayBuffer]) - saveAs(blob, filename) - } - - return ( - Meta Info - } - > - - - Submitted By - - {(function () { - const profile = metadata[submittedBy] - return ( - - ) - })()} - - - - Files - - - {Object.entries(currentFileHashes).map(([filename, hash], index) => { - const isValidHash = creatorFileHashes[filename] === hash - - return ( - - - downloadFile(filename)}> - - - - - {filename} - - {isValidHash && ( - - - - )} - {!isValidHash && ( - - - - )} - - ) - })} - - - - - - - User - Role - Signed Status - - - - {users.map((user) => ( - - ))} - -
-
-
- ) -} - -enum PrevSignatureValidationEnum { - Pending, - Valid, - Invalid -} - -enum UserStatus { - Viewer = 'Viewer', - Awaiting = 'Awaiting Signature', - Signed = 'Signed', - Pending = 'Pending' -} - -type DisplayUserProps = { - meta: Meta - user: User - metadata: { [key: string]: ProfileMetadata } - signedBy: `npub1${string}`[] - nextSigner?: string - getPrevSignersSig: (usersNpub: string) => string | null -} - -const DisplayUser = ({ - meta, - user, - metadata, - signedBy, - nextSigner, - getPrevSignersSig -}: DisplayUserProps) => { - const theme = useTheme() - - const userMeta = metadata[user.pubkey] - const [userStatus, setUserStatus] = useState(UserStatus.Pending) - const [prevSignatureStatus, setPreviousSignatureStatus] = - useState(PrevSignatureValidationEnum.Pending) - - useEffect(() => { - if (user.role === UserRole.viewer) { - setUserStatus(UserStatus.Viewer) - return - } - - // check if user has signed the document - const usersNpub = hexToNpub(user.pubkey) - if (signedBy.includes(usersNpub)) { - setUserStatus(UserStatus.Signed) - return - } - - // check if user is the next signer - if (user.pubkey === nextSigner) { - setUserStatus(UserStatus.Awaiting) - return - } - }, [user, nextSigner, signedBy]) - - useEffect(() => { - const validatePrevSignature = async () => { - const handleNullCase = () => { - setPreviousSignatureStatus(PrevSignatureValidationEnum.Invalid) - return - } - - // get previous signers sig from the content of current signers signed event - const npub = hexToNpub(user.pubkey) - const signedEvent = await parseJson( - meta.docSignatures[npub] - ).catch((err) => { - console.log(`err in parsing the singed event for ${npub}:>> `, err) - toast.error( - err.message || - 'error occurred in parsing the signed event signature event' - ) - return null - }) - - if (!signedEvent) return handleNullCase() - - // now that we have signed event of current signer, we'll extract prevSig from its content - const parsedContent = await parseJson( - signedEvent.content - ).catch((err) => { - console.log( - `an error occurred in parsing the content of signedEvent of ${npub}`, - err - ) - toast.error( - err.message || - `an error occurred in parsing the content of signedEvent of ${npub}` - ) - return null - }) - - if (!parsedContent) return handleNullCase() - - const prevSignersSignature = getPrevSignersSig(npub) - - if (!prevSignersSignature) return handleNullCase() - - setPreviousSignatureStatus( - parsedContent.prevSig === prevSignersSignature - ? PrevSignatureValidationEnum.Valid - : PrevSignatureValidationEnum.Invalid - ) - } - - if (userStatus === UserStatus.Signed) { - validatePrevSignature() - } - }, [userStatus, meta.docSignatures, user.pubkey, getPrevSignersSig]) - - return ( - - - - - {user.role} - - - {userStatus} - {userStatus === UserStatus.Signed && ( - <> - {prevSignatureStatus === PrevSignatureValidationEnum.Valid && ( - - - - )} - {prevSignatureStatus === PrevSignatureValidationEnum.Invalid && ( - - - - )} - - )} - {userStatus === UserStatus.Awaiting && ( - - - - )} - - - - ) -} diff --git a/src/pages/sign/internal/displayMeta.tsx b/src/pages/sign/internal/displayMeta.tsx new file mode 100644 index 0000000..e041e37 --- /dev/null +++ b/src/pages/sign/internal/displayMeta.tsx @@ -0,0 +1,426 @@ +import JSZip from 'jszip' +import { + Meta, + ProfileMetadata, + SignedEventContent, + User, + UserRole +} from '../../../types' +import { + Box, + IconButton, + List, + ListItem, + ListSubheader, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + Tooltip, + Typography, + useTheme +} from '@mui/material' +import { + Download, + CheckCircle, + Cancel, + HourglassTop +} from '@mui/icons-material' +import saveAs from 'file-saver' +import { kinds, Event } from 'nostr-tools' +import { useState, useEffect } from 'react' +import { toast } from 'react-toastify' +import { UserComponent } from '../../../components/username' +import { MetadataController } from '../../../controllers' +import { + npubToHex, + readContentOfZipEntry, + shorten, + hexToNpub, + parseJson +} from '../../../utils' +import styles from '../style.module.scss' + +type DisplayMetaProps = { + meta: Meta + zip: JSZip + submittedBy: string + signers: `npub1${string}`[] + viewers: `npub1${string}`[] + creatorFileHashes: { [key: string]: string } + currentFileHashes: { [key: string]: string | null } + signedBy: `npub1${string}`[] + nextSigner?: string + getPrevSignersSig: (usersNpub: string) => string | null +} + +export const DisplayMeta = ({ + meta, + zip, + submittedBy, + signers, + viewers, + creatorFileHashes, + currentFileHashes, + signedBy, + nextSigner, + getPrevSignersSig +}: DisplayMetaProps) => { + const theme = useTheme() + + const textColor = theme.palette.getContrastText( + theme.palette.background.paper + ) + + const [metadata, setMetadata] = useState<{ [key: string]: ProfileMetadata }>( + {} + ) + const [users, setUsers] = useState([]) + + useEffect(() => { + signers.forEach((signer) => { + const hexKey = npubToHex(signer) + setUsers((prev) => { + if (prev.findIndex((user) => user.pubkey === hexKey) !== -1) return prev + + return [ + ...prev, + { + pubkey: hexKey!, + role: UserRole.signer + } + ] + }) + }) + + viewers.forEach((viewer) => { + const hexKey = npubToHex(viewer) + setUsers((prev) => { + if (prev.findIndex((user) => user.pubkey === hexKey) !== -1) return prev + + return [ + ...prev, + { + pubkey: hexKey!, + role: UserRole.viewer + } + ] + }) + }) + }, [signers, viewers]) + + useEffect(() => { + const metadataController = new MetadataController() + + const hexKeys: string[] = [ + npubToHex(submittedBy)!, + ...users.map((user) => user.pubkey) + ] + + hexKeys.forEach((key) => { + if (!(key in metadata)) { + const handleMetadataEvent = (event: Event) => { + const metadataContent = + metadataController.extractProfileMetadataContent(event) + + if (metadataContent) + setMetadata((prev) => ({ + ...prev, + [key]: metadataContent + })) + } + + metadataController.on(key, (kind: number, event: Event) => { + if (kind === kinds.Metadata) { + handleMetadataEvent(event) + } + }) + + metadataController + .findMetadata(key) + .then((metadataEvent) => { + if (metadataEvent) handleMetadataEvent(metadataEvent) + }) + .catch((err) => { + console.error(`error occurred in finding metadata for: ${key}`, err) + }) + } + }) + }, [users, submittedBy]) + + const downloadFile = async (filename: string) => { + const arrayBuffer = await readContentOfZipEntry( + zip, + `files/${filename}`, + 'arraybuffer' + ) + if (!arrayBuffer) return + + const blob = new Blob([arrayBuffer]) + saveAs(blob, filename) + } + + return ( + Meta Info + } + > + + + Submitted By + + {(function () { + const profile = metadata[submittedBy] + return ( + + ) + })()} + + + + Files + + + {Object.entries(currentFileHashes).map(([filename, hash], index) => { + const isValidHash = creatorFileHashes[filename] === hash + + return ( + + + downloadFile(filename)}> + + + + + {filename} + + {isValidHash && ( + + + + )} + {!isValidHash && ( + + + + )} + + ) + })} + + + + + + + User + Role + Signed Status + + + + {users.map((user) => ( + + ))} + +
+
+
+ ) +} + +enum PrevSignatureValidationEnum { + Pending, + Valid, + Invalid +} + +enum UserStatus { + Viewer = 'Viewer', + Awaiting = 'Awaiting Signature', + Signed = 'Signed', + Pending = 'Pending' +} + +type DisplayUserProps = { + meta: Meta + user: User + metadata: { [key: string]: ProfileMetadata } + signedBy: `npub1${string}`[] + nextSigner?: string + getPrevSignersSig: (usersNpub: string) => string | null +} + +const DisplayUser = ({ + meta, + user, + metadata, + signedBy, + nextSigner, + getPrevSignersSig +}: DisplayUserProps) => { + const theme = useTheme() + + const userMeta = metadata[user.pubkey] + const [userStatus, setUserStatus] = useState(UserStatus.Pending) + const [prevSignatureStatus, setPreviousSignatureStatus] = + useState(PrevSignatureValidationEnum.Pending) + + useEffect(() => { + if (user.role === UserRole.viewer) { + setUserStatus(UserStatus.Viewer) + return + } + + // check if user has signed the document + const usersNpub = hexToNpub(user.pubkey) + if (signedBy.includes(usersNpub)) { + setUserStatus(UserStatus.Signed) + return + } + + // check if user is the next signer + if (user.pubkey === nextSigner) { + setUserStatus(UserStatus.Awaiting) + return + } + }, [user, nextSigner, signedBy]) + + useEffect(() => { + const validatePrevSignature = async () => { + const handleNullCase = () => { + setPreviousSignatureStatus(PrevSignatureValidationEnum.Invalid) + return + } + + // get previous signers sig from the content of current signers signed event + const npub = hexToNpub(user.pubkey) + const signedEvent = await parseJson( + meta.docSignatures[npub] + ).catch((err) => { + console.log(`err in parsing the singed event for ${npub}:>> `, err) + toast.error( + err.message || + 'error occurred in parsing the signed event signature event' + ) + return null + }) + + if (!signedEvent) return handleNullCase() + + // now that we have signed event of current signer, we'll extract prevSig from its content + const parsedContent = await parseJson( + signedEvent.content + ).catch((err) => { + console.log( + `an error occurred in parsing the content of signedEvent of ${npub}`, + err + ) + toast.error( + err.message || + `an error occurred in parsing the content of signedEvent of ${npub}` + ) + return null + }) + + if (!parsedContent) return handleNullCase() + + const prevSignersSignature = getPrevSignersSig(npub) + + if (!prevSignersSignature) return handleNullCase() + + setPreviousSignatureStatus( + parsedContent.prevSig === prevSignersSignature + ? PrevSignatureValidationEnum.Valid + : PrevSignatureValidationEnum.Invalid + ) + } + + if (userStatus === UserStatus.Signed) { + validatePrevSignature() + } + }, [userStatus, meta.docSignatures, user.pubkey, getPrevSignersSig]) + + return ( + + + + + {user.role} + + + {userStatus} + {userStatus === UserStatus.Signed && ( + <> + {prevSignatureStatus === PrevSignatureValidationEnum.Valid && ( + + + + )} + {prevSignatureStatus === PrevSignatureValidationEnum.Invalid && ( + + + + )} + + )} + {userStatus === UserStatus.Awaiting && ( + + + + )} + + + + ) +}