diff --git a/src/components/DisplaySigit/index.tsx b/src/components/DisplaySigit/index.tsx
index 0d7407f..92dc01d 100644
--- a/src/components/DisplaySigit/index.tsx
+++ b/src/components/DisplaySigit/index.tsx
@@ -1,5 +1,5 @@
import { Meta } from '../../types'
-import { SigitCardDisplayInfo, SigitStatus } from '../../utils'
+import { SigitCardDisplayInfo, SigitStatus, SignStatus } from '../../utils'
import { Link } from 'react-router-dom'
import { formatTimestamp, hexToNpub, npubToHex, shorten } from '../../utils'
import { appPublicRoutes, appPrivateRoutes } from '../../routes'
@@ -13,7 +13,6 @@ import {
faFile
} from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
-import { UserAvatar } from '../UserAvatar'
import { UserAvatarGroup } from '../UserAvatarGroup'
import styles from './style.module.scss'
@@ -34,7 +33,8 @@ export const DisplaySigit = ({ meta, parsedMeta }: SigitProps) => {
submittedBy,
signers,
signedStatus,
- fileExtensions
+ fileExtensions,
+ isValid
} = parsedMeta
const { signersStatus } = useSigitMeta(meta)
@@ -62,6 +62,7 @@ export const DisplaySigit = ({ meta, parsedMeta }: SigitProps) => {
const profile = profiles[submittedBy]
return (
{
disableInteractive
>
-
+
)
diff --git a/src/components/UsersDetails.tsx/index.tsx b/src/components/UsersDetails.tsx/index.tsx
index fc7d43d..3681cfd 100644
--- a/src/components/UsersDetails.tsx/index.tsx
+++ b/src/components/UsersDetails.tsx/index.tsx
@@ -9,7 +9,6 @@ import {
shorten,
SignStatus
} from '../../utils'
-import { UserAvatar } from '../UserAvatar'
import { useSigitMeta } from '../../hooks/useSigitMeta'
import { UserAvatarGroup } from '../UserAvatarGroup'
@@ -44,7 +43,8 @@ export const UsersDetails = ({ meta }: UsersDetailsProps) => {
createdAt,
completedAt,
parsedSignatureEvents,
- signedStatus
+ signedStatus,
+ isValid
} = useSigitMeta(meta)
const { usersPubkey } = useSelector((state: State) => state.auth)
const profiles = useSigitProfiles([
@@ -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 ? (
@@ -68,6 +68,7 @@ export const UsersDetails = ({ meta }: UsersDetailsProps) => {
const profile = profiles[submittedBy]
return (
{
disableInteractive
>
-
+
)
@@ -196,14 +201,14 @@ export const UsersDetails = ({ meta }: UsersDetailsProps) => {
{signedStatus}
- {ext.length > 0 ? (
+ {extensions.length > 0 ? (
- {ext.length > 1 ? (
+ {!isSame ? (
<>
Multiple File Types
>
) : (
- getExtensionIconLabel(ext[0])
+ getExtensionIconLabel(extensions[0])
)}
) : (
diff --git a/src/hooks/useSigitMeta.tsx b/src/hooks/useSigitMeta.tsx
index a393824..fea5154 100644
--- a/src/hooks/useSigitMeta.tsx
+++ b/src/hooks/useSigitMeta.tsx
@@ -1,5 +1,10 @@
import { useEffect, useState } from 'react'
-import { CreateSignatureEventContent, Meta, SignedEventContent } from '../types'
+import {
+ CreateSignatureEventContent,
+ DocSignatureEvent,
+ Meta,
+ SignedEventContent
+} from '../types'
import { Mark } from '../types/mark'
import {
fromUnixTimestamp,
@@ -38,7 +43,9 @@ export interface FlatMeta
encryptionKey: string | null
// Parsed Document Signatures
- parsedSignatureEvents: { [signer: `npub1${string}`]: Event }
+ parsedSignatureEvents: {
+ [signer: `npub1${string}`]: DocSignatureEvent
+ }
// Calculated completion time
completedAt?: number
@@ -74,7 +81,7 @@ export const useSigitMeta = (meta: Meta): FlatMeta => {
const [zipUrl, setZipUrl] = useState
('')
const [parsedSignatureEvents, setParsedSignatureEvents] = useState<{
- [signer: `npub1${string}`]: Event
+ [signer: `npub1${string}`]: DocSignatureEvent
}>({})
const [completedAt, setCompletedAt] = useState()
@@ -141,7 +148,10 @@ export const useSigitMeta = (meta: Meta): FlatMeta => {
}
// Temp. map to hold events and signers
- const parsedSignatureEventsMap = new Map<`npub1${string}`, Event>()
+ const parsedSignatureEventsMap = new Map<
+ `npub1${string}`,
+ DocSignatureEvent
+ >()
const signerStatusMap = new Map<`npub1${string}`, SignStatus>()
const getPrevSignerSig = (npub: `npub1${string}`) => {
@@ -183,9 +193,12 @@ export const useSigitMeta = (meta: Meta): FlatMeta => {
if (isValidSignature) {
// get the signature of prev signer from the content of current signers signedEvent
const prevSignersSig = getPrevSignerSig(npub)
-
try {
const obj: SignedEventContent = JSON.parse(event.content)
+ parsedSignatureEventsMap.set(npub, {
+ ...event,
+ parsedContent: obj
+ })
if (
obj.prevSig &&
prevSignersSig &&
diff --git a/src/layouts/StickySideColumns.module.scss b/src/layouts/StickySideColumns.module.scss
index fd99964..7690822 100644
--- a/src/layouts/StickySideColumns.module.scss
+++ b/src/layouts/StickySideColumns.module.scss
@@ -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;
}
diff --git a/src/pages/verify/index.tsx b/src/pages/verify/index.tsx
index 69f7f0b..ceba0eb 100644
--- a/src/pages/verify/index.tsx
+++ b/src/pages/verify/index.tsx
@@ -1,12 +1,16 @@
-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'
-import { CreateSignatureEventContent, Meta } from '../../types'
+import {
+ CreateSignatureEventContent,
+ DocSignatureEvent,
+ Meta
+} from '../../types'
import {
decryptArrayBuffer,
extractMarksFromSignedMeta,
@@ -16,10 +20,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 +31,8 @@ import {
addMarks,
convertToPdfBlob,
convertToPdfFile,
- groupMarksByPage
+ groupMarksByFileNamePage,
+ inPx
} from '../../utils/pdf.ts'
import { State } from '../../store/rootReducer.ts'
import { useSelector } from 'react-redux'
@@ -40,13 +45,101 @@ 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'
+import { Mark } from '../../types/mark.ts'
+
+interface PdfViewProps {
+ files: CurrentUserFile[]
+ currentFile: CurrentUserFile | null
+ parsedSignatureEvents: {
+ [signer: `npub1${string}`]: DocSignatureEvent
+ }
+}
+
+const SlimPdfView = ({
+ files,
+ currentFile,
+ parsedSignatureEvents
+}: 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 (
+
+ {files.map((currentUserFile, i) => {
+ const { hash, filename, pdfFile, id } = currentUserFile
+ const signatureEvents = Object.keys(parsedSignatureEvents)
+ if (!hash) return
+ return (
+ <>
+
(pdfRefs.current[id] = el)}
+ key={filename}
+ className={styles.fileWrapper}
+ >
+ {pdfFile.pages.map((page, i) => {
+ const marks: Mark[] = []
+
+ signatureEvents.forEach((e) => {
+ const m = parsedSignatureEvents[
+ e as `npub1${string}`
+ ].parsedContent?.marks.filter(
+ (m) => m.pdfFileHash == hash && m.location.page == i
+ )
+ if (m) {
+ marks.push(...m)
+ }
+ })
+ return (
+
+
+ {marks.map((m) => {
+ return (
+
+ {m.value}
+
+ )
+ })}
+
+ )
+ })}
+
+
+ {i < files.length - 1 && (
+
+ File Separator
+
+ )}
+ >
+ )
+ })}
+
+ )
+}
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
@@ -54,8 +147,15 @@ export const VerifyPage = () => {
*/
const { uploadedZip, meta } = location.state || {}
- const { submittedBy, zipUrl, encryptionKey, signers, viewers, fileHashes } =
- useSigitMeta(meta)
+ const {
+ submittedBy,
+ zipUrl,
+ encryptionKey,
+ signers,
+ viewers,
+ fileHashes,
+ parsedSignatureEvents
+ } = useSigitMeta(meta)
const profiles = useSigitProfiles([
...(submittedBy ? [submittedBy] : []),
@@ -70,8 +170,23 @@ export const VerifyPage = () => {
const [currentFileHashes, setCurrentFileHashes] = useState<{
[key: string]: string | null
- }>(fileHashes)
+ }>({})
const [files, setFiles] = useState<{ [filename: string]: PdfFile }>({})
+ const [currentFile, setCurrentFile] = useState(null)
+ const [signatureFileHashes, setSignatureFileHashes] = useState<{
+ [key: string]: string
+ }>(fileHashes)
+
+ useEffect(() => {
+ setSignatureFileHashes(fileHashes)
+ }, [fileHashes])
+
+ useEffect(() => {
+ if (Object.entries(files).length > 0) {
+ const tmp = getCurrentUserFiles(files, fileHashes, signatureFileHashes)
+ setCurrentFile(tmp[0])
+ }
+ }, [signatureFileHashes, fileHashes, files])
const usersPubkey = useSelector((state: State) => state.auth.usersPubkey)
const nostrController = NostrController.getInstance()
@@ -203,7 +318,6 @@ export const VerifyPage = () => {
}
}
- console.log('fileHashes :>> ', fileHashes)
setCurrentFileHashes(fileHashes)
setLoadingSpinnerDesc('Parsing meta.json')
@@ -307,10 +421,10 @@ export const VerifyPage = () => {
zip.file('meta.json', stringifiedMeta)
const marks = extractMarksFromSignedMeta(updatedMeta)
- const marksByPage = groupMarksByPage(marks)
+ const marksByPage = groupMarksByFileNamePage(marks)
for (const [fileName, pdf] of Object.entries(files)) {
- const pages = await addMarks(pdf.file, marksByPage)
+ const pages = await addMarks(pdf.file, marksByPage[fileName])
const blob = await convertToPdfBlob(pages)
zip.file(`files/${fileName}`, blob)
}
@@ -414,51 +528,33 @@ export const VerifyPage = () => {
-
- {Object.entries(currentFileHashes).map(
- ([filename, hash], index) => {
- const isValidHash = fileHashes[filename] === hash
-
- return (
-
-
- {filename}
-
- {isValidHash && (
-
-
-
- )}
- {!isValidHash && (
-
-
-
- )}
-
- )
- }
- )}
-
+ {currentFile !== null && (
+
+ )}
{displayExportedBy()}
-
-
-
>
}
right={}
- />
+ >
+
+
)}
>
diff --git a/src/pages/verify/style.module.scss b/src/pages/verify/style.module.scss
index ea6408f..a0dec2f 100644
--- a/src/pages/verify/style.module.scss
+++ b/src/pages/verify/style.module.scss
@@ -50,3 +50,36 @@
}
}
}
+
+.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;
+}
+
+.mark {
+ position: absolute;
+ border: 1px dotted black;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/types/core.ts b/src/types/core.ts
index 609837f..8583d4a 100644
--- a/src/types/core.ts
+++ b/src/types/core.ts
@@ -1,5 +1,6 @@
import { Mark } from './mark'
import { Keys } from '../store/auth/types'
+import { Event } from 'nostr-tools'
export enum UserRole {
signer = 'Signer',
@@ -44,3 +45,7 @@ export interface UserAppData {
keyPair?: Keys // this key pair is used for blossom requests authentication
blossomUrls: string[] // array for storing Urls for the files that stores all the sigits and processedGiftWraps on blossom
}
+
+export interface DocSignatureEvent extends Event {
+ parsedContent?: SignedEventContent
+}
diff --git a/src/utils/file.ts b/src/utils/file.ts
index 94308d5..401d3c4 100644
--- a/src/utils/file.ts
+++ b/src/utils/file.ts
@@ -1,6 +1,6 @@
import { Meta } from '../types'
import { extractMarksFromSignedMeta } from './mark.ts'
-import { addMarks, convertToPdfBlob, groupMarksByPage } from './pdf.ts'
+import { addMarks, convertToPdfBlob, groupMarksByFileNamePage } from './pdf.ts'
import JSZip from 'jszip'
import { PdfFile } from '../types/drawing.ts'
@@ -10,12 +10,12 @@ const getZipWithFiles = async (
): Promise => {
const zip = new JSZip()
const marks = extractMarksFromSignedMeta(meta)
- const marksByPage = groupMarksByPage(marks)
+ const marksByFileNamePage = groupMarksByFileNamePage(marks)
for (const [fileName, pdf] of Object.entries(files)) {
- const pages = await addMarks(pdf.file, marksByPage)
+ const pages = await addMarks(pdf.file, marksByFileNamePage[fileName])
const blob = await convertToPdfBlob(pages)
- zip.file(`/files/${fileName}`, blob)
+ zip.file(`files/${fileName}`, blob)
}
return zip
diff --git a/src/utils/mark.ts b/src/utils/mark.ts
index 18cc3e8..f0cd47e 100644
--- a/src/utils/mark.ts
+++ b/src/utils/mark.ts
@@ -119,7 +119,11 @@ const getUpdatedMark = (
return {
...selectedMark,
currentValue: selectedMarkValue,
- isCompleted: !!selectedMarkValue
+ isCompleted: !!selectedMarkValue,
+ mark: {
+ ...selectedMark.mark,
+ value: selectedMarkValue
+ }
}
}
diff --git a/src/utils/meta.ts b/src/utils/meta.ts
index 4915f19..276f049 100644
--- a/src/utils/meta.ts
+++ b/src/utils/meta.ts
@@ -1,6 +1,6 @@
import { CreateSignatureEventContent, Meta } from '../types'
import { fromUnixTimestamp, parseJson } from '.'
-import { Event } from 'nostr-tools'
+import { Event, verifyEvent } from 'nostr-tools'
import { toast } from 'react-toastify'
export enum SignStatus {
@@ -75,6 +75,7 @@ export interface SigitCardDisplayInfo {
signers: `npub1${string}`[]
fileExtensions: string[]
signedStatus: SigitStatus
+ isValid: boolean
}
/**
@@ -128,12 +129,15 @@ export const extractSigitCardDisplayInfo = async (meta: Meta) => {
const sigitInfo: SigitCardDisplayInfo = {
signers: [],
fileExtensions: [],
- signedStatus: SigitStatus.Partial
+ signedStatus: SigitStatus.Partial,
+ isValid: false
}
try {
const createSignatureEvent = await parseNostrEvent(meta.createSignature)
+ sigitInfo.isValid = verifyEvent(createSignatureEvent)
+
// created_at in nostr events are stored in seconds
sigitInfo.createdAt = fromUnixTimestamp(createSignatureEvent.created_at)
@@ -142,7 +146,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 +173,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 +186,7 @@ export const extractFileExtensions = (fileNames: string[]) => {
return result
}, [])
- return extensions
+ const isSame = extensions.every((ext) => ext === extensions[0])
+
+ return { extensions, isSame }
}
diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts
index 622a259..ce2f132 100644
--- a/src/utils/pdf.ts
+++ b/src/utils/pdf.ts
@@ -71,14 +71,19 @@ const isPdf = (file: File) => file.type.toLowerCase().includes('pdf')
/**
* Reads the pdf file binaries
*/
-const readPdf = (file: File): Promise => {
+const readPdf = (file: File): Promise => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
- reader.onload = (e: any) => {
- const data = e.target.result
-
- resolve(data)
+ reader.onload = (e) => {
+ const data = e.target?.result
+ // Make sure we only resolve for string or ArrayBuffer type
+ // They are accepted by PDFJS.getDocument function
+ if (data && typeof data !== 'undefined') {
+ resolve(data)
+ } else {
+ reject(new Error('File is null or undefined'))
+ }
}
reader.onerror = (err) => {
@@ -94,7 +99,7 @@ const readPdf = (file: File): Promise => {
* Converts pdf to the images
* @param data pdf file bytes
*/
-const pdfToImages = async (data: any): Promise => {
+const pdfToImages = async (data: string | ArrayBuffer): Promise => {
const images: string[] = []
const pdf = await PDFJS.getDocument(data).promise
const canvas = document.createElement('canvas')
@@ -142,7 +147,8 @@ const addMarks = async (
canvas.width = viewport.width
await page.render({ canvasContext: context!, viewport: viewport }).promise
- marksPerPage[i]?.forEach((mark) => draw(mark, context!))
+ if (marksPerPage && Object.hasOwn(marksPerPage, i))
+ marksPerPage[i]?.forEach((mark) => draw(mark, context!))
images.push(canvas.toDataURL())
}
@@ -230,11 +236,11 @@ const convertToPdfFile = async (
* @function scaleMark scales remaining marks in line with SCALE
* @function byPage groups remaining Marks by their page marks.location.page
*/
-const groupMarksByPage = (marks: Mark[]) => {
+const groupMarksByFileNamePage = (marks: Mark[]) => {
return marks
.filter(hasValue)
.map(scaleMark)
- .reduce<{ [key: number]: Mark[] }>(byPage, {})
+ .reduce<{ [filename: string]: { [page: number]: Mark[] } }>(byPage, {})
}
/**
@@ -245,10 +251,21 @@ const groupMarksByPage = (marks: Mark[]) => {
* @param obj - accumulator in the reducer callback
* @param mark - current value, i.e. Mark being examined
*/
-const byPage = (obj: { [key: number]: Mark[] }, mark: Mark) => {
- const key = mark.location.page
- const curGroup = obj[key] ?? []
- return { ...obj, [key]: [...curGroup, mark] }
+const byPage = (
+ obj: { [filename: string]: { [page: number]: Mark[] } },
+ mark: Mark
+) => {
+ const filename = mark.fileName
+ const pageNumber = mark.location.page
+ const pages = obj[filename] ?? {}
+ const marks = pages[pageNumber] ?? []
+ return {
+ ...obj,
+ [filename]: {
+ ...pages,
+ [pageNumber]: [...marks, mark]
+ }
+ }
}
export {
@@ -259,5 +276,5 @@ export {
convertToPdfFile,
addMarks,
convertToPdfBlob,
- groupMarksByPage
+ groupMarksByFileNamePage
}