diff --git a/src/components/FileList/index.tsx b/src/components/FileList/index.tsx
new file mode 100644
index 0000000..ba69332
--- /dev/null
+++ b/src/components/FileList/index.tsx
@@ -0,0 +1,41 @@
+import { CurrentUserFile } from '../../types/file.ts'
+import styles from './style.module.scss'
+import { Button } from '@mui/material'
+
+interface FileListProps {
+ files: CurrentUserFile[]
+ currentFile: CurrentUserFile
+ setCurrentFile: (file: CurrentUserFile) => void
+ handleDownload: () => void
+}
+
+const FileList = ({
+ files,
+ currentFile,
+ setCurrentFile,
+ handleDownload
+}: FileListProps) => {
+ const isActive = (file: CurrentUserFile) => file.id === currentFile.id
+ return (
+
+
+
+ {files.map((file: CurrentUserFile) => (
+ - setCurrentFile(file)}
+ >
+ {file.id}
+ {file.filename}
+
+ ))}
+
+
+
+ )
+}
+
+export default FileList
diff --git a/src/components/FileList/style.module.scss b/src/components/FileList/style.module.scss
new file mode 100644
index 0000000..bbf9311
--- /dev/null
+++ b/src/components/FileList/style.module.scss
@@ -0,0 +1,78 @@
+.container {
+ border-radius: 4px;
+ background: white;
+ padding: 15px;
+ display: flex;
+ flex-direction: column;
+ grid-gap: 0px;
+}
+
+.files {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ grid-gap: 15px;
+ max-height: 350px;
+ overflow: auto;
+ padding: 0 5px 0 0;
+ margin: 0 -5px 0 0;
+}
+
+.files::-webkit-scrollbar {
+ width: 10px;
+}
+
+.files::-webkit-scrollbar-track {
+ background-color: rgba(0,0,0,0.15);
+}
+
+.files::-webkit-scrollbar-thumb {
+ max-width: 10px;
+ border-radius: 2px;
+ background-color: #4c82a3;
+ cursor: grab;
+}
+
+.fileItem {
+ transition: ease 0.2s;
+ display: flex;
+ flex-direction: row;
+ grid-gap: 10px;
+ border-radius: 4px;
+ overflow: hidden;
+ background: #ffffff;
+ padding: 5px 10px;
+ align-items: center;
+ color: rgba(0,0,0,0.5);
+ cursor: pointer;
+ flex-grow: 1;
+ font-size: 16px;
+ font-weight: 500;
+}
+
+.fileItem:hover {
+ transition: ease 0.2s;
+ background: #4c82a3;
+ color: white;
+
+ &.active {
+ background: #4c82a3;
+ color: white;
+ }
+}
+
+.fileName {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ -webkit-line-clamp: 1;
+}
+
+.fileNumber {
+ font-size: 14px;
+ font-weight: 500;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
\ No newline at end of file
diff --git a/src/components/PDFView/PdfMarking.tsx b/src/components/PDFView/PdfMarking.tsx
index b0e0748..fc49f79 100644
--- a/src/components/PDFView/PdfMarking.tsx
+++ b/src/components/PDFView/PdfMarking.tsx
@@ -1,24 +1,26 @@
import PdfView from './index.tsx'
import MarkFormField from '../MarkFormField'
-import { PdfFile } from '../../types/drawing.ts'
import { CurrentUserMark, Mark } from '../../types/mark.ts'
import React, { useState, useEffect } from 'react'
import {
findNextIncompleteCurrentUserMark,
getUpdatedMark,
- isCurrentUserMarksComplete,
updateCurrentUserMarks
} from '../../utils'
import { EMPTY } from '../../utils/const.ts'
import { Container } from '../Container'
-import styles from '../../pages/sign/style.module.scss'
+import signPageStyles from '../../pages/sign/style.module.scss'
+import styles from './style.module.scss'
+import { CurrentUserFile } from '../../types/file.ts'
+import FileList from '../FileList'
interface PdfMarkingProps {
- files: { pdfFile: PdfFile; filename: string; hash: string | null }[]
+ files: CurrentUserFile[]
currentUserMarks: CurrentUserMark[]
setIsReadyToSign: (isReadyToSign: boolean) => void
setCurrentUserMarks: (currentUserMarks: CurrentUserMark[]) => void
setUpdatedMarks: (markToUpdate: Mark) => void
+ handleDownload: () => void
}
/**
@@ -33,10 +35,12 @@ const PdfMarking = (props: PdfMarkingProps) => {
currentUserMarks,
setIsReadyToSign,
setCurrentUserMarks,
- setUpdatedMarks
+ setUpdatedMarks,
+ handleDownload
} = props
const [selectedMark, setSelectedMark] = useState(null)
const [selectedMarkValue, setSelectedMarkValue] = useState('')
+ const [currentFile, setCurrentFile] = useState(null)
useEffect(() => {
if (selectedMark === null && currentUserMarks.length > 0) {
@@ -47,6 +51,12 @@ const PdfMarking = (props: PdfMarkingProps) => {
}
}, [currentUserMarks, selectedMark])
+ useEffect(() => {
+ if (currentFile === null && files.length > 0) {
+ setCurrentFile(files[0])
+ }
+ }, [files, currentFile])
+
const handleMarkClick = (id: number) => {
const nextMark = currentUserMarks.find((mark) => mark.mark.id === id)
setSelectedMark(nextMark!)
@@ -101,16 +111,30 @@ const PdfMarking = (props: PdfMarkingProps) => {
return (
<>
-
- {currentUserMarks?.length > 0 && (
-
- )}
+
+
+
+ {currentFile !== null && (
+
+ )}
+
+ {currentUserMarks?.length > 0 && (
+
+ )}
+
{selectedMark !== null && (
void
selectedMarkValue: string
@@ -14,29 +14,38 @@ interface PdfViewProps {
/**
* Responsible for rendering Pdf files.
*/
-const PdfView = ({ files, currentUserMarks, handleMarkClick, selectedMarkValue, selectedMark }: PdfViewProps) => {
- const filterByFile = (currentUserMarks: CurrentUserMark[], hash: string): CurrentUserMark[] => {
- return currentUserMarks.filter((currentUserMark) => currentUserMark.mark.pdfFileHash === hash)
+const PdfView = ({
+ files,
+ currentUserMarks,
+ handleMarkClick,
+ selectedMarkValue,
+ selectedMark
+}: PdfViewProps) => {
+ const filterByFile = (
+ currentUserMarks: CurrentUserMark[],
+ hash: string
+ ): CurrentUserMark[] => {
+ return currentUserMarks.filter(
+ (currentUserMark) => currentUserMark.mark.pdfFileHash === hash
+ )
}
return (
- {
- files.map(({ pdfFile, hash }, i) => {
- if (!hash) return
- return (
- {
+ if (!hash) return
+ return (
+
- )
- })
- }
+ )
+ })}
)
}
-export default PdfView;
\ No newline at end of file
+export default PdfView
diff --git a/src/components/PDFView/style.module.scss b/src/components/PDFView/style.module.scss
index 2e6e519..3ebbc85 100644
--- a/src/components/PDFView/style.module.scss
+++ b/src/components/PDFView/style.module.scss
@@ -13,4 +13,17 @@
max-width: 100%;
max-height: 100%;
object-fit: contain; /* Ensure the image fits within the container */
-}
\ No newline at end of file
+}
+
+.container {
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+}
+
+.pdfView {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+}
diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx
index 07c385e..02ac415 100644
--- a/src/pages/sign/index.tsx
+++ b/src/pages/sign/index.tsx
@@ -16,13 +16,16 @@ import { State } from '../../store/rootReducer'
import { CreateSignatureEventContent, Meta, SignedEvent } from '../../types'
import {
decryptArrayBuffer,
- encryptArrayBuffer, extractMarksFromSignedMeta,
+ encryptArrayBuffer,
+ extractMarksFromSignedMeta,
extractZipUrlAndEncryptionKey,
generateEncryptionKey,
- generateKeysFile, getFilesWithHashes,
+ generateKeysFile,
+ getCurrentUserFiles,
getHash,
hexToNpub,
- isOnline, loadZip,
+ isOnline,
+ loadZip,
now,
npubToHex,
parseJson,
@@ -41,9 +44,12 @@ import { getLastSignersSig } from '../../utils/sign.ts'
import {
filterMarksByPubkey,
getCurrentUserMarks,
- isCurrentUserMarksComplete, updateMarks
+ isCurrentUserMarksComplete,
+ updateMarks
} from '../../utils'
import PdfMarking from '../../components/PDFView/PdfMarking.tsx'
+import { getZipWithFiles } from '../../utils/file.ts'
+import { ARRAY_BUFFER, DEFLATE } from '../../utils/const.ts'
enum SignedStatus {
Fully_Signed,
User_Is_Next_Signer,
@@ -81,7 +87,7 @@ export const SignPage = () => {
const [signers, setSigners] = useState<`npub1${string}`[]>([])
const [viewers, setViewers] = useState<`npub1${string}`[]>([])
- const [marks, setMarks] = useState([])
+ const [marks, setMarks] = useState([])
const [creatorFileHashes, setCreatorFileHashes] = useState<{
[key: string]: string
}>({})
@@ -100,8 +106,10 @@ export const SignPage = () => {
const [authUrl, setAuthUrl] = useState()
const nostrController = NostrController.getInstance()
- const [currentUserMarks, setCurrentUserMarks] = useState([]);
- const [isReadyToSign, setIsReadyToSign] = useState(false);
+ const [currentUserMarks, setCurrentUserMarks] = useState(
+ []
+ )
+ const [isReadyToSign, setIsReadyToSign] = useState(false)
useEffect(() => {
if (signers.length > 0) {
@@ -192,13 +200,16 @@ export const SignPage = () => {
setViewers(createSignatureContent.viewers)
setCreatorFileHashes(createSignatureContent.fileHashes)
setSubmittedBy(createSignatureEvent.pubkey)
- setMarks(createSignatureContent.markConfig);
+ setMarks(createSignatureContent.markConfig)
if (usersPubkey) {
- const metaMarks = filterMarksByPubkey(createSignatureContent.markConfig, usersPubkey!)
+ const metaMarks = filterMarksByPubkey(
+ createSignatureContent.markConfig,
+ usersPubkey!
+ )
const signedMarks = extractMarksFromSignedMeta(meta)
- const currentUserMarks = getCurrentUserMarks(metaMarks, signedMarks);
- setCurrentUserMarks(currentUserMarks);
+ const currentUserMarks = getCurrentUserMarks(metaMarks, signedMarks)
+ setCurrentUserMarks(currentUserMarks)
// setCurrentUserMark(findNextCurrentUserMark(currentUserMarks) || null)
setIsReadyToSign(isCurrentUserMarksComplete(currentUserMarks))
}
@@ -307,7 +318,7 @@ export const SignPage = () => {
)
if (arrayBuffer) {
- files[fileName] = await convertToPdfFile(arrayBuffer, fileName);
+ files[fileName] = await convertToPdfFile(arrayBuffer, fileName)
const hash = await getHash(arrayBuffer)
if (hash) {
@@ -348,7 +359,7 @@ export const SignPage = () => {
const decrypt = async (file: File) => {
setLoadingSpinnerDesc('Decrypting file')
- const zip = await loadZip(file);
+ const zip = await loadZip(file)
if (!zip) return
const parsedKeysJson = await parseKeysJson(zip)
@@ -414,6 +425,27 @@ export const SignPage = () => {
return null
}
+ const handleDownload = async () => {
+ if (Object.entries(files).length === 0 || !meta || !usersPubkey) return
+ setLoadingSpinnerDesc('Generating file')
+ try {
+ const zip = await getZipWithFiles(meta, files)
+ const arrayBuffer = await zip.generateAsync({
+ type: ARRAY_BUFFER,
+ compression: DEFLATE,
+ compressionOptions: {
+ level: 6
+ }
+ })
+ if (!arrayBuffer) return
+ const blob = new Blob([arrayBuffer])
+ saveAs(blob, `exported-${now()}.sigit.zip`)
+ } catch (error: any) {
+ console.log('error in zip:>> ', error)
+ toast.error(error.message || 'Error occurred in generating zip file')
+ }
+ }
+
const handleDecryptedArrayBuffer = async (arrayBuffer: ArrayBuffer) => {
const decryptedZipFile = new File([arrayBuffer], 'decrypted.zip')
@@ -439,7 +471,7 @@ export const SignPage = () => {
)
if (arrayBuffer) {
- files[fileName] = await convertToPdfFile(arrayBuffer, fileName);
+ files[fileName] = await convertToPdfFile(arrayBuffer, fileName)
const hash = await getHash(arrayBuffer)
if (hash) {
@@ -520,7 +552,10 @@ export const SignPage = () => {
}
// Sign the event for the meta file
- const signEventForMeta = async (signerContent: { prevSig: string, marks: Mark[] }) => {
+ const signEventForMeta = async (signerContent: {
+ prevSig: string
+ marks: Mark[]
+ }) => {
return await signEventForMetaFile(
JSON.stringify(signerContent),
nostrController,
@@ -529,8 +564,8 @@ export const SignPage = () => {
}
const getSignerMarksForMeta = (): Mark[] | undefined => {
- if (currentUserMarks.length === 0) return;
- return currentUserMarks.map(( { mark }: CurrentUserMark) => mark);
+ if (currentUserMarks.length === 0) return
+ return currentUserMarks.map(({ mark }: CurrentUserMark) => mark)
}
// Update the meta signatures
@@ -600,13 +635,9 @@ export const SignPage = () => {
if (!arraybuffer) return null
- return new File(
- [new Blob([arraybuffer])],
- `${unixNow}.sigit.zip`,
- {
- type: 'application/zip'
- }
- )
+ return new File([new Blob([arraybuffer])], `${unixNow}.sigit.zip`, {
+ type: 'application/zip'
+ })
}
// Handle errors during zip file generation
@@ -694,7 +725,7 @@ export const SignPage = () => {
setIsLoading(true)
setLoadingSpinnerDesc('Signing nostr event')
- if (!meta) return;
+ if (!meta) return
const prevSig = getLastSignersSig(meta, signers)
if (!prevSig) return
@@ -918,11 +949,14 @@ export const SignPage = () => {
)
}
- return
+ return (
+
+ )
}
diff --git a/src/pages/verify/index.tsx b/src/pages/verify/index.tsx
index 6290c02..b39fe74 100644
--- a/src/pages/verify/index.tsx
+++ b/src/pages/verify/index.tsx
@@ -23,14 +23,17 @@ import {
SignedEventContent
} from '../../types'
import {
- decryptArrayBuffer, extractMarksFromSignedMeta,
+ decryptArrayBuffer,
+ extractMarksFromSignedMeta,
extractZipUrlAndEncryptionKey,
getHash,
- hexToNpub, now,
+ hexToNpub,
+ now,
npubToHex,
parseJson,
readContentOfZipEntry,
- shorten, signEventForMetaFile
+ shorten,
+ signEventForMetaFile
} from '../../utils'
import styles from './style.module.scss'
import { Cancel, CheckCircle } from '@mui/icons-material'
@@ -41,7 +44,7 @@ import {
addMarks,
convertToPdfBlob,
convertToPdfFile,
- groupMarksByPage,
+ groupMarksByPage
} from '../../utils/pdf.ts'
import { State } from '../../store/rootReducer.ts'
import { useSelector } from 'react-redux'
@@ -78,7 +81,7 @@ export const VerifyPage = () => {
const [currentFileHashes, setCurrentFileHashes] = useState<{
[key: string]: string | null
}>({})
- const [files, setFiles] = useState<{ [filename: string]: PdfFile}>({})
+ const [files, setFiles] = useState<{ [filename: string]: PdfFile }>({})
const [metadata, setMetadata] = useState<{ [key: string]: ProfileMetadata }>(
{}
@@ -155,7 +158,10 @@ export const VerifyPage = () => {
)
if (arrayBuffer) {
- files[fileName] = await convertToPdfFile(arrayBuffer, fileName!)
+ files[fileName] = await convertToPdfFile(
+ arrayBuffer,
+ fileName!
+ )
const hash = await getHash(arrayBuffer)
if (hash) {
@@ -169,7 +175,6 @@ export const VerifyPage = () => {
setCurrentFileHashes(fileHashes)
setFiles(files)
-
setSigners(createSignatureContent.signers)
setViewers(createSignatureContent.viewers)
setCreatorFileHashes(createSignatureContent.fileHashes)
@@ -177,8 +182,6 @@ export const VerifyPage = () => {
setMeta(metaInNavState)
setIsLoading(false)
-
-
}
})
.catch((err) => {
@@ -381,7 +384,7 @@ export const VerifyPage = () => {
}
const handleExport = async () => {
- if (Object.entries(files).length === 0 ||!meta ||!usersPubkey) return;
+ if (Object.entries(files).length === 0 || !meta || !usersPubkey) return
const usersNpub = hexToNpub(usersPubkey)
if (
@@ -395,10 +398,8 @@ export const VerifyPage = () => {
setIsLoading(true)
setLoadingSpinnerDesc('Signing nostr event')
- if (!meta) return;
-
const prevSig = getLastSignersSig(meta, signers)
- if (!prevSig) return;
+ if (!prevSig) return
const signedEvent = await signEventForMetaFile(
JSON.stringify({ prevSig }),
@@ -406,10 +407,10 @@ export const VerifyPage = () => {
setIsLoading
)
- if (!signedEvent) return;
+ if (!signedEvent) return
const exportSignature = JSON.stringify(signedEvent, null, 2)
- const updatedMeta = {...meta, exportSignature }
+ const updatedMeta = { ...meta, exportSignature }
const stringifiedMeta = JSON.stringify(updatedMeta, null, 2)
const zip = new JSZip()
diff --git a/src/types/file.ts b/src/types/file.ts
new file mode 100644
index 0000000..0f2c127
--- /dev/null
+++ b/src/types/file.ts
@@ -0,0 +1,8 @@
+import { PdfFile } from './drawing.ts'
+
+export interface CurrentUserFile {
+ id: number
+ pdfFile: PdfFile
+ filename: string
+ hash?: string
+}
diff --git a/src/utils/const.ts b/src/utils/const.ts
index 2a97dfe..511f418 100644
--- a/src/utils/const.ts
+++ b/src/utils/const.ts
@@ -6,3 +6,5 @@ export const MARK_TYPE_TRANSLATION: { [key: string]: string } = {
}
export const SIGN: string = 'Sign'
export const NEXT: string = 'Next'
+export const ARRAY_BUFFER = 'arraybuffer'
+export const DEFLATE = 'DEFLATE'
diff --git a/src/utils/file.ts b/src/utils/file.ts
new file mode 100644
index 0000000..94308d5
--- /dev/null
+++ b/src/utils/file.ts
@@ -0,0 +1,24 @@
+import { Meta } from '../types'
+import { extractMarksFromSignedMeta } from './mark.ts'
+import { addMarks, convertToPdfBlob, groupMarksByPage } from './pdf.ts'
+import JSZip from 'jszip'
+import { PdfFile } from '../types/drawing.ts'
+
+const getZipWithFiles = async (
+ meta: Meta,
+ files: { [filename: string]: PdfFile }
+): Promise => {
+ const zip = new JSZip()
+ const marks = extractMarksFromSignedMeta(meta)
+ const marksByPage = groupMarksByPage(marks)
+
+ for (const [fileName, pdf] of Object.entries(files)) {
+ const pages = await addMarks(pdf.file, marksByPage)
+ const blob = await convertToPdfBlob(pages)
+ zip.file(`/files/${fileName}`, blob)
+ }
+
+ return zip
+}
+
+export { getZipWithFiles }
diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts
index 70b6539..a684d78 100644
--- a/src/utils/pdf.ts
+++ b/src/utils/pdf.ts
@@ -3,13 +3,14 @@ import * as PDFJS from 'pdfjs-dist'
import { PDFDocument } from 'pdf-lib'
import { Mark } from '../types/mark.ts'
-PDFJS.GlobalWorkerOptions.workerSrc = 'node_modules/pdfjs-dist/build/pdf.worker.mjs';
+PDFJS.GlobalWorkerOptions.workerSrc =
+ 'node_modules/pdfjs-dist/build/pdf.worker.mjs'
/**
* Scale between the PDF page's natural size and rendered size
* @constant {number}
*/
-const SCALE: number = 3;
+const SCALE: number = 3
/**
* Defined font size used when generating a PDF. Currently it is difficult to fully
* correlate font size used at the time of filling in / drawing on the PDF
@@ -17,20 +18,20 @@ const SCALE: number = 3;
* This should be fixed going forward.
* Switching to PDF-Lib will most likely make this problem redundant.
*/
-const FONT_SIZE: number = 40;
+const FONT_SIZE: number = 40
/**
* Current font type used when generating a PDF.
*/
-const FONT_TYPE: string = 'Arial';
+const FONT_TYPE: string = 'Arial'
/**
* Converts a PDF ArrayBuffer to a generic PDF File
* @param arrayBuffer of a PDF
* @param fileName identifier of the pdf file
*/
-const toFile = (arrayBuffer: ArrayBuffer, fileName: string) : File => {
- const blob = new Blob([arrayBuffer], { type: "application/pdf" });
- return new File([blob], fileName, { type: "application/pdf" });
+const toFile = (arrayBuffer: ArrayBuffer, fileName: string): File => {
+ const blob = new Blob([arrayBuffer], { type: 'application/pdf' })
+ return new File([blob], fileName, { type: 'application/pdf' })
}
/**
@@ -50,42 +51,40 @@ const toPdfFile = async (file: File): Promise => {
* @return PdfFile[] - an array of Sigit's internal Pdf File type
*/
const toPdfFiles = async (selectedFiles: File[]): Promise => {
- return Promise.all(selectedFiles
- .filter(isPdf)
- .map(toPdfFile));
+ return Promise.all(selectedFiles.filter(isPdf).map(toPdfFile))
}
/**
* A utility that transforms a drawing coordinate number into a CSS-compatible string
* @param coordinate
*/
-const inPx = (coordinate: number): string => `${coordinate}px`;
+const inPx = (coordinate: number): string => `${coordinate}px`
/**
* A utility that checks if a given file is of the pdf type
* @param file
*/
-const isPdf = (file: File) => file.type.toLowerCase().includes('pdf');
+const isPdf = (file: File) => file.type.toLowerCase().includes('pdf')
/**
* Reads the pdf file binaries
*/
const readPdf = (file: File): Promise => {
return new Promise((resolve, reject) => {
- const reader = new FileReader();
+ const reader = new FileReader()
reader.onload = (e: any) => {
const data = e.target.result
resolve(data)
- };
+ }
reader.onerror = (err) => {
console.error('err', err)
reject(err)
- };
+ }
- reader.readAsDataURL(file);
+ reader.readAsDataURL(file)
})
}
@@ -94,26 +93,28 @@ const readPdf = (file: File): Promise => {
* @param data pdf file bytes
*/
const pdfToImages = async (data: any): Promise => {
- const images: string[] = [];
- const pdf = await PDFJS.getDocument(data).promise;
- const canvas = document.createElement("canvas");
+ const images: string[] = []
+ const pdf = await PDFJS.getDocument(data).promise
+ const canvas = document.createElement('canvas')
for (let i = 0; i < pdf.numPages; i++) {
- const page = await pdf.getPage(i + 1);
- const viewport = page.getViewport({ scale: SCALE });
- const context = canvas.getContext("2d");
- canvas.height = viewport.height;
- canvas.width = viewport.width;
- await page.render({ canvasContext: context!, viewport: viewport }).promise;
- images.push(canvas.toDataURL());
+ const page = await pdf.getPage(i + 1)
+ const viewport = page.getViewport({ scale: SCALE })
+ const context = canvas.getContext('2d')
+ canvas.height = viewport.height
+ canvas.width = viewport.width
+ await page.render({ canvasContext: context!, viewport: viewport }).promise
+ images.push(canvas.toDataURL())
}
- return Promise.resolve(images.map((image) => {
- return {
- image,
- drawnFields: []
- }
- }))
+ return Promise.resolve(
+ images.map((image) => {
+ return {
+ image,
+ drawnFields: []
+ }
+ })
+ )
}
/**
@@ -121,34 +122,37 @@ const pdfToImages = async (data: any): Promise => {
* Returns an array of encoded images where each image is a representation
* of a PDF page with completed and signed marks from all users
*/
-const addMarks = async (file: File, marksPerPage: {[key: string]: Mark[]}) => {
- const p = await readPdf(file);
- const pdf = await PDFJS.getDocument(p).promise;
- const canvas = document.createElement("canvas");
+const addMarks = async (
+ file: File,
+ marksPerPage: { [key: string]: Mark[] }
+) => {
+ const p = await readPdf(file)
+ const pdf = await PDFJS.getDocument(p).promise
+ const canvas = document.createElement('canvas')
- const images: string[] = [];
+ const images: string[] = []
- for (let i = 0; i< pdf.numPages; i++) {
- const page = await pdf.getPage(i+1)
- const viewport = page.getViewport({ scale: SCALE });
- const context = canvas.getContext("2d");
- canvas.height = viewport.height;
- canvas.width = viewport.width;
- await page.render({ canvasContext: context!, viewport: viewport }).promise;
+ for (let i = 0; i < pdf.numPages; i++) {
+ const page = await pdf.getPage(i + 1)
+ const viewport = page.getViewport({ scale: SCALE })
+ const context = canvas.getContext('2d')
+ canvas.height = viewport.height
+ canvas.width = viewport.width
+ await page.render({ canvasContext: context!, viewport: viewport }).promise
- marksPerPage[i].forEach(mark => draw(mark, context!))
+ marksPerPage[i]?.forEach((mark) => draw(mark, context!))
- images.push(canvas.toDataURL());
+ images.push(canvas.toDataURL())
}
- return Promise.resolve(images);
+ return Promise.resolve(images)
}
/**
* Utility to scale mark in line with the PDF-to-PNG scale
*/
const scaleMark = (mark: Mark): Mark => {
- const { location } = mark;
+ const { location } = mark
return {
...mark,
location: {
@@ -165,7 +169,7 @@ const scaleMark = (mark: Mark): Mark => {
* Utility to check if a Mark has value
* @param mark
*/
-const hasValue = (mark: Mark): boolean => !!mark.value;
+const hasValue = (mark: Mark): boolean => !!mark.value
/**
* Draws a Mark on a Canvas representation of a PDF Page
@@ -173,14 +177,14 @@ const hasValue = (mark: Mark): boolean => !!mark.value;
* @param ctx a Canvas representation of a specific PDF Page
*/
const draw = (mark: Mark, ctx: CanvasRenderingContext2D) => {
- const { location } = mark;
+ const { location } = mark
- ctx!.font = FONT_SIZE + 'px ' + FONT_TYPE;
- ctx!.fillStyle = 'black';
- const textMetrics = ctx!.measureText(mark.value!);
- const textX = location.left + (location.width - textMetrics.width) / 2;
- const textY = location.top + (location.height + parseInt(ctx!.font)) / 2;
- ctx!.fillText(mark.value!, textX, textY);
+ ctx!.font = FONT_SIZE + 'px ' + FONT_TYPE
+ ctx!.fillStyle = 'black'
+ const textMetrics = ctx!.measureText(mark.value!)
+ const textX = location.left + (location.width - textMetrics.width) / 2
+ const textY = location.top + (location.height + parseInt(ctx!.font)) / 2
+ ctx!.fillText(mark.value!, textX, textY)
}
/**
@@ -188,7 +192,7 @@ const draw = (mark: Mark, ctx: CanvasRenderingContext2D) => {
* @param markedPdfPages
*/
const convertToPdfBlob = async (markedPdfPages: string[]): Promise => {
- const pdfDoc = await PDFDocument.create();
+ const pdfDoc = await PDFDocument.create()
for (const page of markedPdfPages) {
const pngImage = await pdfDoc.embedPng(page)
@@ -203,7 +207,6 @@ const convertToPdfBlob = async (markedPdfPages: string[]): Promise => {
const pdfBytes = await pdfDoc.save()
return new Blob([pdfBytes], { type: 'application/pdf' })
-
}
/**
@@ -211,9 +214,12 @@ const convertToPdfBlob = async (markedPdfPages: string[]): Promise => {
* @param arrayBuffer
* @param fileName
*/
-const convertToPdfFile = async (arrayBuffer: ArrayBuffer, fileName: string): Promise => {
- const file = toFile(arrayBuffer, fileName);
- return toPdfFile(file);
+const convertToPdfFile = async (
+ arrayBuffer: ArrayBuffer,
+ fileName: string
+): Promise => {
+ const file = toFile(arrayBuffer, fileName)
+ return toPdfFile(file)
}
/**
@@ -226,7 +232,7 @@ const groupMarksByPage = (marks: Mark[]) => {
return marks
.filter(hasValue)
.map(scaleMark)
- .reduce<{[key: number]: Mark[]}>(byPage, {})
+ .reduce<{ [key: number]: Mark[] }>(byPage, {})
}
/**
@@ -237,11 +243,10 @@ 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: { [key: number]: Mark[] }, mark: Mark) => {
+ const key = mark.location.page
+ const curGroup = obj[key] ?? []
+ return { ...obj, [key]: [...curGroup, mark] }
}
export {
@@ -252,5 +257,5 @@ export {
convertToPdfFile,
addMarks,
convertToPdfBlob,
- groupMarksByPage,
-}
\ No newline at end of file
+ groupMarksByPage
+}
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index ed830a2..e201c41 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -1,4 +1,5 @@
import { PdfFile } from '../types/drawing.ts'
+import { CurrentUserFile } from '../types/file.ts'
export const compareObjects = (
obj1: object | null | undefined,
@@ -72,11 +73,16 @@ export const timeout = (ms: number = 60000) => {
* @param files
* @param fileHashes
*/
-export const getFilesWithHashes = (
- files: { [filename: string ]: PdfFile },
+export const getCurrentUserFiles = (
+ files: { [filename: string]: PdfFile },
fileHashes: { [key: string]: string | null }
- ) => {
- return Object.entries(files).map(([filename, pdfFile]) => {
- return { pdfFile, filename, hash: fileHashes[filename] }
+): CurrentUserFile[] => {
+ return Object.entries(files).map(([filename, pdfFile], index) => {
+ return {
+ pdfFile,
+ filename,
+ id: index + 1,
+ ...(!!fileHashes[filename] && { hash: fileHashes[filename]! })
+ }
})
}