Releasing new design #161
@ -56,7 +56,7 @@ export const UsersDetails = ({ meta }: UsersDetailsProps) => {
|
||||
typeof usersPubkey !== 'undefined' &&
|
||||
signers.includes(hexToNpub(usersPubkey))
|
||||
|
||||
const ext = extractFileExtensions(Object.keys(fileHashes))
|
||||
const { extensions, isSame } = extractFileExtensions(Object.keys(fileHashes))
|
||||
|
||||
return submittedBy ? (
|
||||
<div className={styles.container}>
|
||||
@ -196,14 +196,14 @@ export const UsersDetails = ({ meta }: UsersDetailsProps) => {
|
||||
<span className={styles.detailsItem}>
|
||||
<FontAwesomeIcon icon={faEye} /> {signedStatus}
|
||||
</span>
|
||||
{ext.length > 0 ? (
|
||||
{extensions.length > 0 ? (
|
||||
<span className={styles.detailsItem}>
|
||||
{ext.length > 1 ? (
|
||||
{!isSame ? (
|
||||
<>
|
||||
<FontAwesomeIcon icon={faFile} /> Multiple File Types
|
||||
</>
|
||||
) : (
|
||||
getExtensionIconLabel(ext[0])
|
||||
getExtensionIconLabel(extensions[0])
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
|
@ -23,7 +23,11 @@
|
||||
grid-gap: 15px;
|
||||
}
|
||||
.content {
|
||||
max-width: 550px;
|
||||
width: 550px;
|
||||
padding: 10px;
|
||||
border: 10px solid $overlay-background-color;
|
||||
border-radius: 4px;
|
||||
|
||||
max-width: 590px;
|
||||
width: 590px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Box, Button, Tooltip, Typography, useTheme } from '@mui/material'
|
||||
import { Box, Button, Divider, Tooltip, Typography } from '@mui/material'
|
||||
import JSZip from 'jszip'
|
||||
import { MuiFileInput } from 'mui-file-input'
|
||||
import { Event, verifyEvent } from 'nostr-tools'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||
import { NostrController } from '../../controllers'
|
||||
@ -16,10 +16,10 @@ import {
|
||||
parseJson,
|
||||
readContentOfZipEntry,
|
||||
signEventForMetaFile,
|
||||
shorten
|
||||
shorten,
|
||||
getCurrentUserFiles
|
||||
} from '../../utils'
|
||||
import styles from './style.module.scss'
|
||||
import { Cancel, CheckCircle } from '@mui/icons-material'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import axios from 'axios'
|
||||
import { PdfFile } from '../../types/drawing.ts'
|
||||
@ -27,7 +27,8 @@ import {
|
||||
addMarks,
|
||||
convertToPdfBlob,
|
||||
convertToPdfFile,
|
||||
groupMarksByPage
|
||||
groupMarksByPage,
|
||||
inPx
|
||||
} from '../../utils/pdf.ts'
|
||||
import { State } from '../../store/rootReducer.ts'
|
||||
import { useSelector } from 'react-redux'
|
||||
@ -40,13 +41,77 @@ 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 { CurrentUserFile } from '../../types/file.ts'
|
||||
|
||||
interface PdfViewProps {
|
||||
files: CurrentUserFile[]
|
||||
currentFile: CurrentUserFile | null
|
||||
}
|
||||
|
||||
const SlimPdfView = ({ files, currentFile }: PdfViewProps) => {
|
||||
const pdfRefs = useRef<(HTMLDivElement | null)[]>([])
|
||||
useEffect(() => {
|
||||
if (currentFile !== null && !!pdfRefs.current[currentFile.id]) {
|
||||
pdfRefs.current[currentFile.id]?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'end'
|
||||
})
|
||||
}
|
||||
}, [currentFile])
|
||||
return (
|
||||
<div className={styles.view}>
|
||||
{files.map((currentUserFile, i) => {
|
||||
const { hash, filename, pdfFile, id } = currentUserFile
|
||||
if (!hash) return
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
id={filename}
|
||||
ref={(el) => (pdfRefs.current[id] = el)}
|
||||
key={filename}
|
||||
className={styles.fileWrapper}
|
||||
>
|
||||
{pdfFile.pages.map((page, i) => {
|
||||
return (
|
||||
<div className={styles.imageWrapper} key={i}>
|
||||
<img draggable="false" src={page.image} />
|
||||
{page.drawnFields.map((f, i) => (
|
||||
<div
|
||||
key={i}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
border: '1px dotted black',
|
||||
left: inPx(f.left),
|
||||
top: inPx(f.top),
|
||||
width: inPx(f.width),
|
||||
height: inPx(f.height)
|
||||
}}
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{i < files.length - 1 && (
|
||||
<Divider
|
||||
sx={{
|
||||
fontSize: '12px',
|
||||
color: 'rgba(0,0,0,0.15)'
|
||||
}}
|
||||
>
|
||||
File Separator
|
||||
</Divider>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const VerifyPage = () => {
|
||||
const theme = useTheme()
|
||||
const textColor = theme.palette.getContrastText(
|
||||
theme.palette.background.paper
|
||||
)
|
||||
|
||||
const location = useLocation()
|
||||
/**
|
||||
* uploadedZip will be received from home page when a user uploads a sigit zip wrapper that contains meta.json
|
||||
@ -57,6 +122,8 @@ export const VerifyPage = () => {
|
||||
const { submittedBy, zipUrl, encryptionKey, signers, viewers, fileHashes } =
|
||||
useSigitMeta(meta)
|
||||
|
||||
console.log('----------', meta)
|
||||
|
||||
const profiles = useSigitProfiles([
|
||||
...(submittedBy ? [submittedBy] : []),
|
||||
...signers,
|
||||
@ -72,6 +139,15 @@ export const VerifyPage = () => {
|
||||
[key: string]: string | null
|
||||
}>(fileHashes)
|
||||
const [files, setFiles] = useState<{ [filename: string]: PdfFile }>({})
|
||||
const [currentFile, setCurrentFile] = useState<CurrentUserFile | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.entries(files).length > 0) {
|
||||
const tmp = getCurrentUserFiles(files, fileHashes)
|
||||
|
||||
setCurrentFile(tmp[0])
|
||||
}
|
||||
}, [fileHashes, files])
|
||||
|
||||
const usersPubkey = useSelector((state: State) => state.auth.usersPubkey)
|
||||
const nostrController = NostrController.getInstance()
|
||||
@ -414,51 +490,24 @@ export const VerifyPage = () => {
|
||||
<StickySideColumns
|
||||
left={
|
||||
<>
|
||||
<Box className={styles.filesWrapper}>
|
||||
{Object.entries(currentFileHashes).map(
|
||||
([filename, hash], index) => {
|
||||
const isValidHash = fileHashes[filename] === hash
|
||||
|
||||
return (
|
||||
<Box key={`file-${index}`} className={styles.file}>
|
||||
<Typography
|
||||
component="label"
|
||||
sx={{
|
||||
color: textColor,
|
||||
flexGrow: 1
|
||||
}}
|
||||
>
|
||||
{filename}
|
||||
</Typography>
|
||||
{isValidHash && (
|
||||
<Tooltip title="File integrity check passed" arrow>
|
||||
<CheckCircle
|
||||
sx={{ color: theme.palette.success.light }}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!isValidHash && (
|
||||
<Tooltip title="File integrity check failed" arrow>
|
||||
<Cancel
|
||||
sx={{ color: theme.palette.error.main }}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</Box>
|
||||
{currentFile !== null && (
|
||||
<FileList
|
||||
files={getCurrentUserFiles(files, currentFileHashes)}
|
||||
currentFile={currentFile}
|
||||
setCurrentFile={setCurrentFile}
|
||||
handleDownload={handleExport}
|
||||
/>
|
||||
)}
|
||||
{displayExportedBy()}
|
||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button onClick={handleExport} variant="contained">
|
||||
Export Sigit
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
right={<UsersDetails meta={meta} />}
|
||||
/>
|
||||
>
|
||||
<SlimPdfView
|
||||
currentFile={currentFile}
|
||||
files={getCurrentUserFiles(files, currentFileHashes)}
|
||||
/>
|
||||
</StickySideColumns>
|
||||
)}
|
||||
</Container>
|
||||
</>
|
||||
|
@ -50,3 +50,27 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.view {
|
||||
width: 550px;
|
||||
max-width: 550px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.imageWrapper {
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.fileWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ export const extractSigitCardDisplayInfo = async (meta: Meta) => {
|
||||
)
|
||||
|
||||
const files = Object.keys(createSignatureContent.fileHashes)
|
||||
const extensions = extractFileExtensions(files)
|
||||
const { extensions } = extractFileExtensions(files)
|
||||
|
||||
const signedBy = Object.keys(meta.docSignatures) as `npub1${string}`[]
|
||||
const isCompletelySigned = createSignatureContent.signers.every((signer) =>
|
||||
@ -169,6 +169,10 @@ export const extractSigitCardDisplayInfo = async (meta: Meta) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileNames - List of filenames to check
|
||||
* @returns List of extensions and if all are same
|
||||
*/
|
||||
export const extractFileExtensions = (fileNames: string[]) => {
|
||||
const extensions = fileNames.reduce((result: string[], file: string) => {
|
||||
const extension = file.split('.').pop()
|
||||
@ -178,5 +182,7 @@ export const extractFileExtensions = (fileNames: string[]) => {
|
||||
return result
|
||||
}, [])
|
||||
|
||||
return extensions
|
||||
const isSame = extensions.every((ext) => ext === extensions[0])
|
||||
|
||||
return { extensions, isSame }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user