fix: profiles matadata and verify page offline flow #202
@ -1,16 +1,11 @@
|
|||||||
import { Box, Button, Tooltip, Typography } from '@mui/material'
|
import { Box, Button, Typography } from '@mui/material'
|
||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import { MuiFileInput } from 'mui-file-input'
|
import { MuiFileInput } from 'mui-file-input'
|
||||||
import { Event, verifyEvent } from 'nostr-tools'
|
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||||
import { NostrController } from '../../controllers'
|
import { NostrController } from '../../controllers'
|
||||||
import {
|
import { DocSignatureEvent, Meta } from '../../types'
|
||||||
CreateSignatureEventContent,
|
|
||||||
DocSignatureEvent,
|
|
||||||
Meta
|
|
||||||
} from '../../types'
|
|
||||||
import {
|
import {
|
||||||
decryptArrayBuffer,
|
decryptArrayBuffer,
|
||||||
extractMarksFromSignedMeta,
|
extractMarksFromSignedMeta,
|
||||||
@ -20,7 +15,6 @@ import {
|
|||||||
parseJson,
|
parseJson,
|
||||||
readContentOfZipEntry,
|
readContentOfZipEntry,
|
||||||
signEventForMetaFile,
|
signEventForMetaFile,
|
||||||
shorten,
|
|
||||||
getCurrentUserFiles
|
getCurrentUserFiles
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
@ -42,9 +36,6 @@ import { Container } from '../../components/Container'
|
|||||||
import { useSigitMeta } from '../../hooks/useSigitMeta.tsx'
|
import { useSigitMeta } from '../../hooks/useSigitMeta.tsx'
|
||||||
import { StickySideColumns } from '../../layouts/StickySideColumns.tsx'
|
import { StickySideColumns } from '../../layouts/StickySideColumns.tsx'
|
||||||
import { UsersDetails } from '../../components/UsersDetails.tsx/index.tsx'
|
import { UsersDetails } from '../../components/UsersDetails.tsx/index.tsx'
|
||||||
import { UserAvatar } from '../../components/UserAvatar/index.tsx'
|
|
||||||
import { useSigitProfiles } from '../../hooks/useSigitProfiles.tsx'
|
|
||||||
import { TooltipChild } from '../../components/TooltipChild.tsx'
|
|
||||||
import FileList from '../../components/FileList'
|
import FileList from '../../components/FileList'
|
||||||
import { CurrentUserFile } from '../../types/file.ts'
|
import { CurrentUserFile } from '../../types/file.ts'
|
||||||
import { Mark } from '../../types/mark.ts'
|
import { Mark } from '../../types/mark.ts'
|
||||||
@ -163,12 +154,26 @@ const SlimPdfView = ({
|
|||||||
|
|
||||||
export const VerifyPage = () => {
|
export const VerifyPage = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const usersPubkey = useSelector((state: State) => state.auth.usersPubkey)
|
||||||
|
|
||||||
|
const nostrController = NostrController.getInstance()
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* uploadedZip will be received from home page when a user uploads a sigit zip wrapper that contains meta.json
|
* uploadedZip will be received from home page when a user uploads a sigit zip wrapper that contains meta.json
|
||||||
* meta will be received in navigation from create & home page in online mode
|
* meta will be received in navigation from create & home page in online mode
|
||||||
*/
|
*/
|
||||||
const { uploadedZip, meta } = location.state || {}
|
const { uploadedZip, meta: metaInNavState } = location.state || {}
|
||||||
|
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
||||||
|
useEffect(() => {
|
||||||
|
if (uploadedZip) {
|
||||||
|
setSelectedFile(uploadedZip)
|
||||||
|
}
|
||||||
|
}, [uploadedZip])
|
||||||
|
|
||||||
|
const [meta, setMeta] = useState<Meta>(metaInNavState)
|
||||||
const {
|
const {
|
||||||
submittedBy,
|
submittedBy,
|
||||||
zipUrl,
|
zipUrl,
|
||||||
@ -179,44 +184,22 @@ export const VerifyPage = () => {
|
|||||||
parsedSignatureEvents
|
parsedSignatureEvents
|
||||||
} = useSigitMeta(meta)
|
} = useSigitMeta(meta)
|
||||||
|
|
||||||
const profiles = useSigitProfiles([
|
const [files, setFiles] = useState<{ [filename: string]: SigitFile }>({})
|
||||||
...(submittedBy ? [submittedBy] : []),
|
|
||||||
...signers,
|
|
||||||
...viewers
|
|
||||||
])
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
|
||||||
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
|
|
||||||
|
|
||||||
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
|
||||||
|
|
||||||
|
const [currentFile, setCurrentFile] = useState<CurrentUserFile | null>(null)
|
||||||
const [currentFileHashes, setCurrentFileHashes] = useState<{
|
const [currentFileHashes, setCurrentFileHashes] = useState<{
|
||||||
[key: string]: string | null
|
[key: string]: string | null
|
||||||
}>({})
|
}>({})
|
||||||
const [files, setFiles] = useState<{ [filename: string]: SigitFile }>({})
|
|
||||||
const [currentFile, setCurrentFile] = useState<CurrentUserFile | null>(null)
|
|
||||||
const [signatureFileHashes, setSignatureFileHashes] = useState<{
|
|
||||||
[key: string]: string
|
|
||||||
}>(fileHashes)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setSignatureFileHashes(fileHashes)
|
|
||||||
}, [fileHashes])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.entries(files).length > 0) {
|
if (Object.entries(files).length > 0) {
|
||||||
const tmp = getCurrentUserFiles(files, fileHashes, signatureFileHashes)
|
const tmp = getCurrentUserFiles(files, currentFileHashes, fileHashes)
|
||||||
setCurrentFile(tmp[0])
|
setCurrentFile(tmp[0])
|
||||||
}
|
}
|
||||||
}, [signatureFileHashes, fileHashes, files])
|
}, [currentFileHashes, fileHashes, files])
|
||||||
|
|
||||||
const usersPubkey = useSelector((state: State) => state.auth.usersPubkey)
|
|
||||||
const nostrController = NostrController.getInstance()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (uploadedZip) {
|
if (metaInNavState && encryptionKey) {
|
||||||
setSelectedFile(uploadedZip)
|
|
||||||
} else if (meta && encryptionKey) {
|
|
||||||
const processSigit = async () => {
|
const processSigit = async () => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
|
||||||
@ -301,7 +284,7 @@ export const VerifyPage = () => {
|
|||||||
|
|
||||||
processSigit()
|
processSigit()
|
||||||
}
|
}
|
||||||
}, [encryptionKey, meta, uploadedZip, zipUrl])
|
}, [encryptionKey, metaInNavState, zipUrl])
|
||||||
|
|
||||||
const handleVerify = async () => {
|
const handleVerify = async () => {
|
||||||
if (!selectedFile) return
|
if (!selectedFile) return
|
||||||
@ -315,6 +298,7 @@ export const VerifyPage = () => {
|
|||||||
|
|
||||||
if (!zip) return
|
if (!zip) return
|
||||||
|
|
||||||
|
const files: { [filename: string]: SigitFile } = {}
|
||||||
const fileHashes: { [key: string]: string | null } = {}
|
const fileHashes: { [key: string]: string | null } = {}
|
||||||
const fileNames = Object.values(zip.files)
|
const fileNames = Object.values(zip.files)
|
||||||
.filter((entry) => entry.name.startsWith('files/') && !entry.dir)
|
.filter((entry) => entry.name.startsWith('files/') && !entry.dir)
|
||||||
@ -322,24 +306,27 @@ export const VerifyPage = () => {
|
|||||||
|
|
||||||
// generate hashes for all entries in files folder of zipArchive
|
// generate hashes for all entries in files folder of zipArchive
|
||||||
// these hashes can be used to verify the originality of files
|
// these hashes can be used to verify the originality of files
|
||||||
for (const fileName of fileNames) {
|
for (const zipFilePath of fileNames) {
|
||||||
const arrayBuffer = await readContentOfZipEntry(
|
const arrayBuffer = await readContentOfZipEntry(
|
||||||
zip,
|
zip,
|
||||||
fileName,
|
zipFilePath,
|
||||||
'arraybuffer'
|
'arraybuffer'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const fileName = zipFilePath.replace(/^files\//, '')
|
||||||
if (arrayBuffer) {
|
if (arrayBuffer) {
|
||||||
|
files[fileName] = await convertToSigitFile(arrayBuffer, fileName)
|
||||||
const hash = await getHash(arrayBuffer)
|
const hash = await getHash(arrayBuffer)
|
||||||
|
|
||||||
if (hash) {
|
if (hash) {
|
||||||
fileHashes[fileName.replace(/^files\//, '')] = hash
|
fileHashes[fileName] = hash
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fileHashes[fileName.replace(/^files\//, '')] = null
|
fileHashes[fileName] = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFiles(files)
|
||||||
setCurrentFileHashes(fileHashes)
|
setCurrentFileHashes(fileHashes)
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Parsing meta.json')
|
setLoadingSpinnerDesc('Parsing meta.json')
|
||||||
@ -368,43 +355,7 @@ export const VerifyPage = () => {
|
|||||||
|
|
||||||
if (!parsedMetaJson) return
|
if (!parsedMetaJson) return
|
||||||
|
|
||||||
const createSignatureEvent = await parseJson<Event>(
|
setMeta(parsedMetaJson)
|
||||||
parsedMetaJson.createSignature
|
|
||||||
).catch((err) => {
|
|
||||||
console.log('err in parsing the createSignature event:>> ', err)
|
|
||||||
toast.error(
|
|
||||||
err.message || 'error occurred in parsing the create signature event'
|
|
||||||
)
|
|
||||||
setIsLoading(false)
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!createSignatureEvent) return
|
|
||||||
|
|
||||||
const isValidCreateSignature = verifyEvent(createSignatureEvent)
|
|
||||||
|
|
||||||
if (!isValidCreateSignature) {
|
|
||||||
toast.error('Create signature is invalid')
|
|
||||||
setIsLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const createSignatureContent = await parseJson<CreateSignatureEventContent>(
|
|
||||||
createSignatureEvent.content
|
|
||||||
).catch((err) => {
|
|
||||||
console.log(
|
|
||||||
`err in parsing the createSignature event's content :>> `,
|
|
||||||
err
|
|
||||||
)
|
|
||||||
toast.error(
|
|
||||||
err.message ||
|
|
||||||
`error occurred in parsing the create signature event's content`
|
|
||||||
)
|
|
||||||
setIsLoading(false)
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!createSignatureContent) return
|
|
||||||
|
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
@ -479,47 +430,6 @@ export const VerifyPage = () => {
|
|||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayExportedBy = () => {
|
|
||||||
if (!meta || !meta.exportSignature) return null
|
|
||||||
|
|
||||||
const exportSignatureString = meta.exportSignature
|
|
||||||
|
|
||||||
try {
|
|
||||||
const exportSignatureEvent = JSON.parse(exportSignatureString) as Event
|
|
||||||
|
|
||||||
if (verifyEvent(exportSignatureEvent)) {
|
|
||||||
const exportedBy = exportSignatureEvent.pubkey
|
|
||||||
const profile = profiles[exportedBy]
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
title={
|
|
||||||
profile?.display_name ||
|
|
||||||
profile?.name ||
|
|
||||||
shorten(hexToNpub(exportedBy))
|
|
||||||
}
|
|
||||||
placement="top"
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
>
|
|
||||||
<TooltipChild>
|
|
||||||
<UserAvatar pubkey={exportedBy} image={profile?.picture} />
|
|
||||||
</TooltipChild>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
toast.error(`Invalid export signature!`)
|
|
||||||
return (
|
|
||||||
<Typography component="label" sx={{ color: 'red' }}>
|
|
||||||
Invalid export signature
|
|
||||||
</Typography>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`An error occurred wile parsing exportSignature`, error)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
|
{isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
|
||||||
@ -554,22 +464,19 @@ export const VerifyPage = () => {
|
|||||||
{meta && (
|
{meta && (
|
||||||
<StickySideColumns
|
<StickySideColumns
|
||||||
left={
|
left={
|
||||||
<>
|
currentFile !== null && (
|
||||||
{currentFile !== null && (
|
|
||||||
<FileList
|
<FileList
|
||||||
files={getCurrentUserFiles(
|
files={getCurrentUserFiles(
|
||||||
files,
|
files,
|
||||||
currentFileHashes,
|
currentFileHashes,
|
||||||
signatureFileHashes
|
fileHashes
|
||||||
)}
|
)}
|
||||||
currentFile={currentFile}
|
currentFile={currentFile}
|
||||||
setCurrentFile={setCurrentFile}
|
setCurrentFile={setCurrentFile}
|
||||||
handleDownload={handleExport}
|
handleDownload={handleExport}
|
||||||
downloadLabel="Download Sigit"
|
downloadLabel="Download Sigit"
|
||||||
/>
|
/>
|
||||||
)}
|
)
|
||||||
{displayExportedBy()}
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
right={<UsersDetails meta={meta} />}
|
right={<UsersDetails meta={meta} />}
|
||||||
leftIcon={faFileDownload}
|
leftIcon={faFileDownload}
|
||||||
@ -578,11 +485,7 @@ export const VerifyPage = () => {
|
|||||||
>
|
>
|
||||||
<SlimPdfView
|
<SlimPdfView
|
||||||
currentFile={currentFile}
|
currentFile={currentFile}
|
||||||
files={getCurrentUserFiles(
|
files={getCurrentUserFiles(files, currentFileHashes, fileHashes)}
|
||||||
files,
|
|
||||||
currentFileHashes,
|
|
||||||
signatureFileHashes
|
|
||||||
)}
|
|
||||||
parsedSignatureEvents={parsedSignatureEvents}
|
parsedSignatureEvents={parsedSignatureEvents}
|
||||||
/>
|
/>
|
||||||
</StickySideColumns>
|
</StickySideColumns>
|
||||||
|
Loading…
Reference in New Issue
Block a user