diff --git a/src/components/DrawPDFFields/index.tsx b/src/components/DrawPDFFields/index.tsx
index c582d2d..7d25996 100644
--- a/src/components/DrawPDFFields/index.tsx
+++ b/src/components/DrawPDFFields/index.tsx
@@ -101,7 +101,7 @@ export const DrawPDFFields = (props: Props) => {
* It is re rendered and visible right away
*
* @param event Mouse event
- * @param page PdfPage where press happened
+ * @param page PdfItem where press happened
*/
const onMouseDown = (event: any, page: PdfPage) => {
// Proceed only if left click
@@ -154,7 +154,7 @@ export const DrawPDFFields = (props: Props) => {
* After {@link onMouseDown} create an drawing element, this function gets called on every pixel moved
* which alters the newly created drawing element, resizing it while mouse move
* @param event Mouse event
- * @param page PdfPage where moving is happening
+ * @param page PdfItem where moving is happening
*/
const onMouseMove = (event: any, page: PdfPage) => {
if (mouseState.clicked && selectedTool) {
diff --git a/src/components/PDFView/Mark.tsx b/src/components/PDFView/Mark.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/PDFView/PdfItem.tsx b/src/components/PDFView/PdfItem.tsx
new file mode 100644
index 0000000..7ffd000
--- /dev/null
+++ b/src/components/PDFView/PdfItem.tsx
@@ -0,0 +1,28 @@
+import { PdfFile } from '../../types/drawing.ts'
+import { MarkConfigDetails} from '../../types/mark.ts'
+import PdfPageItem from './PdfPageItem.tsx';
+
+interface PdfItemProps {
+ pdfFile: PdfFile
+ markConfigDetails: MarkConfigDetails[]
+}
+
+const PdfItem = ({ pdfFile, markConfigDetails }: PdfItemProps) => {
+ const filterMarkConfigDetails = (i: number) => {
+ return markConfigDetails.filter(
+ (details) => details.markLocation.page === i);
+ }
+ return (
+ pdfFile.pages.map((page, i) => {
+ console.log('page: ', page);
+ return (
+
+ )
+ }))
+}
+
+export default PdfItem
\ No newline at end of file
diff --git a/src/components/PDFView/PdfMarkItem.tsx b/src/components/PDFView/PdfMarkItem.tsx
new file mode 100644
index 0000000..a170c45
--- /dev/null
+++ b/src/components/PDFView/PdfMarkItem.tsx
@@ -0,0 +1,23 @@
+import { MarkLocation } from '../../types/mark.ts'
+import styles from '../DrawPDFFields/style.module.scss'
+import { inPx } from '../../utils/pdf.ts'
+
+interface PdfMarkItemProps {
+ markLocation: MarkLocation
+}
+
+const PdfMarkItem = ({ markLocation }: PdfMarkItemProps) => {
+ return (
+
+ )
+}
+
+export default PdfMarkItem
\ No newline at end of file
diff --git a/src/components/PDFView/PdfPageItem.tsx b/src/components/PDFView/PdfPageItem.tsx
new file mode 100644
index 0000000..5176fe9
--- /dev/null
+++ b/src/components/PDFView/PdfPageItem.tsx
@@ -0,0 +1,30 @@
+import styles from '../DrawPDFFields/style.module.scss'
+import { PdfPage } from '../../types/drawing.ts'
+import { MarkConfigDetails, MarkLocation } from '../../types/mark.ts'
+import PdfMarkItem from './PdfMarkItem.tsx'
+import { useState } from 'react';
+interface PdfPageProps {
+ page: PdfPage
+ markConfigDetails: MarkConfigDetails[]
+}
+
+const PdfPageItem = ({ page, markConfigDetails }: PdfPageProps) => {
+ const [currentMark, setCurrentMark] = useState(null);
+
+ return (
+
+
+ {markConfigDetails.map((detail, i) => (
+
+ ))}
+
+ )
+}
+
+export default PdfPageItem
\ No newline at end of file
diff --git a/src/components/PDFView/index.tsx b/src/components/PDFView/index.tsx
new file mode 100644
index 0000000..6c6113c
--- /dev/null
+++ b/src/components/PDFView/index.tsx
@@ -0,0 +1,45 @@
+import { PdfFile } from '../../types/drawing.ts'
+import { Box } from '@mui/material'
+import PdfItem from './PdfItem.tsx'
+import { MarkConfig, MarkConfigDetails } from '../../types/mark.ts'
+import { State } from '../../store/rootReducer'
+import { useSelector } from 'react-redux';
+import { hexToNpub, npubToHex } from '../../utils'
+
+interface PdfViewProps {
+ files: { [filename: string]: PdfFile },
+ fileHashes: { [key: string]: string | null },
+ markConfig: MarkConfig,
+}
+
+const PdfView = (props: PdfViewProps) => {
+ console.log('current file hashes: ', props.fileHashes)
+ const usersPubkey = useSelector((state: State) => state.auth.usersPubkey);
+ if (!usersPubkey) return;
+ console.log(props.markConfig[hexToNpub(usersPubkey)]);
+
+ console.log('users pubkey: ', usersPubkey);
+ console.log('mark config: ', props.markConfig);
+
+ const getMarkConfigDetails = (fileName: string): MarkConfigDetails[] | undefined => {
+ const fileHash = props.fileHashes[fileName];
+ if (!fileHash) return;
+ return props.markConfig[hexToNpub(usersPubkey)][fileHash];
+ }
+ const { files } = props;
+ return (
+
+ {Object.entries(files)
+ .filter(([name]) => !!getMarkConfigDetails(name))
+ .map(([name, file], i) => (
+
+ ))}
+
+ )
+
+}
+
+export default PdfView;
\ No newline at end of file
diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx
index e914172..215ca83 100644
--- a/src/pages/create/index.tsx
+++ b/src/pages/create/index.tsx
@@ -350,9 +350,17 @@ export const CreatePage = () => {
if (!markConfig[drawnField.counterpart]) markConfig[drawnField.counterpart] = {}
if (!markConfig[drawnField.counterpart][fileHash]) markConfig[drawnField.counterpart][fileHash] = []
+ console.log('drawn field: ', drawnField);
+
markConfig[drawnField.counterpart][fileHash].push({
markType: drawnField.type,
- markLocation: `P:${pageIndex};X:${drawnField.left};Y:${drawnField.top}`
+ markLocation: {
+ page: pageIndex,
+ top: drawnField.top,
+ left: drawnField.left,
+ height: drawnField.height,
+ width: drawnField.width,
+ }
})
})
})
@@ -510,6 +518,8 @@ export const CreatePage = () => {
const viewers = users.filter((user) => user.role === UserRole.viewer)
const markConfig = createMarkConfig(fileHashes)
+ console.log('mark config: ', markConfig)
+
const content: CreateSignatureEventContent = {
signers: signers.map((signer) => hexToNpub(signer.pubkey)),
viewers: viewers.map((viewer) => hexToNpub(viewer.pubkey)),
@@ -519,6 +529,8 @@ export const CreatePage = () => {
title
}
+ console.log('content: ', content)
+
setLoadingSpinnerDesc('Signing nostr event for create signature')
const createSignature = await signEventForMetaFile(
diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx
index 5204ea7..c7f2866 100644
--- a/src/pages/sign/index.tsx
+++ b/src/pages/sign/index.tsx
@@ -22,7 +22,7 @@ import {
generateKeysFile,
getHash,
hexToNpub,
- isOnline,
+ isOnline, loadZip,
now,
npubToHex,
parseJson,
@@ -33,6 +33,10 @@ import {
} from '../../utils'
import { DisplayMeta } from './internal/displayMeta'
import styles from './style.module.scss'
+import { PdfFile } from '../../types/drawing.ts'
+import { toFile, toPdfFile } from '../../utils/pdf.ts'
+import PdfView from '../../components/PDFView'
+import { MarkConfig } from '../../types/mark.ts'
enum SignedStatus {
Fully_Signed,
User_Is_Next_Signer,
@@ -58,7 +62,7 @@ export const SignPage = () => {
const [selectedFile, setSelectedFile] = useState(null)
- const [files, setFiles] = useState<{ [filename: string]: ArrayBuffer }>({})
+ const [files, setFiles] = useState<{ [filename: string]: PdfFile }>({})
const [isLoading, setIsLoading] = useState(true)
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
@@ -70,6 +74,7 @@ export const SignPage = () => {
const [signers, setSigners] = useState<`npub1${string}`[]>([])
const [viewers, setViewers] = useState<`npub1${string}`[]>([])
+ const [markConfig, setMarkConfig] = useState(null)
const [creatorFileHashes, setCreatorFileHashes] = useState<{
[key: string]: string
}>({})
@@ -178,6 +183,9 @@ export const SignPage = () => {
setViewers(createSignatureContent.viewers)
setCreatorFileHashes(createSignatureContent.fileHashes)
setSubmittedBy(createSignatureEvent.pubkey)
+ setMarkConfig(createSignatureContent.markConfig);
+
+ console.log('createSignatureContent', createSignatureContent)
setSignedBy(Object.keys(meta.docSignatures) as `npub1${string}`[])
}
@@ -262,16 +270,13 @@ export const SignPage = () => {
if (!decrypted) return
- const zip = await JSZip.loadAsync(decrypted).catch((err) => {
- console.log('err in loading zip file :>> ', err)
- toast.error(err.message || 'An error occurred in loading zip file.')
+ const zip = await loadZip(decrypted)
+ if (!zip) {
setIsLoading(false)
- return null
- })
+ return
+ }
- if (!zip) return
-
- const files: { [filename: string]: ArrayBuffer } = {}
+ const files: { [filename: string]: PdfFile } = {}
const fileHashes: { [key: string]: string | null } = {}
const fileNames = Object.values(zip.files).map((entry) => entry.name)
@@ -285,7 +290,7 @@ export const SignPage = () => {
)
if (arrayBuffer) {
- files[fileName] = arrayBuffer
+ files[fileName] = await convertToPdfFile(arrayBuffer, fileName);
const hash = await getHash(arrayBuffer)
if (hash) {
@@ -296,10 +301,17 @@ export const SignPage = () => {
}
}
+ console.log('processed files: ', files);
+
setFiles(files)
setCurrentFileHashes(fileHashes)
}
+ const convertToPdfFile = async (arrayBuffer: ArrayBuffer, fileName: string) => {
+ const file = toFile(arrayBuffer, fileName);
+ return toPdfFile(file);
+ }
+
const parseKeysJson = async (zip: JSZip) => {
const keysFileContent = await readContentOfZipEntry(
zip,
@@ -323,11 +335,7 @@ export const SignPage = () => {
const decrypt = async (file: File) => {
setLoadingSpinnerDesc('Decrypting file')
- const zip = await JSZip.loadAsync(file).catch((err) => {
- console.log('err in loading zip file :>> ', err)
- toast.error(err.message || 'An error occurred in loading zip file.')
- return null
- })
+ const zip = await loadZip(file);
if (!zip) return
const parsedKeysJson = await parseKeysJson(zip)
@@ -398,32 +406,27 @@ export const SignPage = () => {
setLoadingSpinnerDesc('Parsing zip file')
- const zip = await JSZip.loadAsync(decryptedZipFile).catch((err) => {
- console.log('err in loading zip file :>> ', err)
- toast.error(err.message || 'An error occurred in loading zip file.')
- return null
- })
-
+ const zip = await loadZip(decryptedZipFile)
if (!zip) return
- const files: { [filename: string]: ArrayBuffer } = {}
+ const files: { [filename: string]: PdfFile } = {}
const fileHashes: { [key: string]: string | null } = {}
const fileNames = Object.values(zip.files)
.filter((entry) => entry.name.startsWith('files/') && !entry.dir)
.map((entry) => entry.name)
+ .map((entry) => entry.replace(/^files\//, ''))
// generate hashes for all entries in files folder of zipArchive
// these hashes can be used to verify the originality of files
- for (let fileName of fileNames) {
+ for (const fileName of fileNames) {
const arrayBuffer = await readContentOfZipEntry(
zip,
fileName,
'arraybuffer'
)
- fileName = fileName.replace(/^files\//, '')
if (arrayBuffer) {
- files[fileName] = arrayBuffer
+ files[fileName] = await convertToPdfFile(arrayBuffer, fileName);
const hash = await getHash(arrayBuffer)
if (hash) {
@@ -434,6 +437,8 @@ export const SignPage = () => {
}
}
+ console.log('processed files: ', files);
+
setFiles(files)
setCurrentFileHashes(fileHashes)
@@ -916,6 +921,17 @@ export const SignPage = () => {
)}
>
)}
+ {markConfig && ()}
+
+
+
+
+
+
>
)
diff --git a/src/pages/sign/internal/displayMeta.tsx b/src/pages/sign/internal/displayMeta.tsx
index 730347e..ab32c31 100644
--- a/src/pages/sign/internal/displayMeta.tsx
+++ b/src/pages/sign/internal/displayMeta.tsx
@@ -34,10 +34,11 @@ import { UserComponent } from '../../../components/username'
import { MetadataController } from '../../../controllers'
import { npubToHex, shorten, hexToNpub, parseJson } from '../../../utils'
import styles from '../style.module.scss'
+import { PdfFile } from '../../../types/drawing.ts'
type DisplayMetaProps = {
meta: Meta
- files: { [filename: string]: ArrayBuffer }
+ files: { [filename: string]: PdfFile }
submittedBy: string
signers: `npub1${string}`[]
viewers: `npub1${string}`[]
@@ -143,7 +144,7 @@ export const DisplayMeta = ({
}, [users, submittedBy])
const downloadFile = async (filename: string) => {
- const arrayBuffer = files[filename]
+ const arrayBuffer = await files[filename].file.arrayBuffer()
if (!arrayBuffer) return
const blob = new Blob([arrayBuffer])
diff --git a/src/pages/sign/style.module.scss b/src/pages/sign/style.module.scss
index 283f3d8..d3b1086 100644
--- a/src/pages/sign/style.module.scss
+++ b/src/pages/sign/style.module.scss
@@ -47,4 +47,36 @@
@extend .user;
}
}
+
+ .fixedBottomForm {
+ position: fixed;
+ bottom: 0;
+ width: 50%;
+ border-top: 1px solid #ccc;
+ box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1);
+ padding: 10px 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+ }
+
+ .fixedBottomForm input[type="text"] {
+ width: 80%;
+ padding: 10px;
+ font-size: 16px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ }
+
+ .fixedBottomForm button {
+ background-color: #3f3d56;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ font-size: 16px;
+ margin-left: 10px;
+ border-radius: 4px;
+ cursor: pointer;
+ }
}
diff --git a/src/types/mark.ts b/src/types/mark.ts
index d3c85c7..57e1512 100644
--- a/src/types/mark.ts
+++ b/src/types/mark.ts
@@ -37,7 +37,15 @@ export interface MarkConfigDetails {
/**
* Coordinates in format: X:10;Y:50
*/
- markLocation: string;
+ markLocation: MarkLocation;
+}
+
+export interface MarkLocation {
+ top: number;
+ left: number;
+ height: number;
+ width: number;
+ page: number;
}
// Creator Meta Object Example
diff --git a/src/types/zip.ts b/src/types/zip.ts
index a2ef3e7..b4c97ac 100644
--- a/src/types/zip.ts
+++ b/src/types/zip.ts
@@ -1,4 +1,4 @@
-export interface OutputByType {
+ export interface OutputByType {
base64: string
string: string
text: string
@@ -10,4 +10,17 @@ export interface OutputByType {
nodebuffer: Buffer
}
+interface InputByType {
+ base64: string;
+ string: string;
+ text: string;
+ binarystring: string;
+ array: number[];
+ uint8array: Uint8Array;
+ arraybuffer: ArrayBuffer;
+ blob: Blob;
+ stream: NodeJS.ReadableStream;
+}
+
export type OutputType = keyof OutputByType
+export type InputFileFormat = InputByType[keyof InputByType] | Promise;
\ No newline at end of file
diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts
index a73f39f..341289a 100644
--- a/src/utils/pdf.ts
+++ b/src/utils/pdf.ts
@@ -20,6 +20,8 @@ const toPdfFiles = async (selectedFiles: File[]): Promise => {
.map(toPdfFile));
}
+const inPx = (coordinate: number): string => `${coordinate}px`;
+
const isPdf = (file: File) => file.type.toLowerCase().includes('pdf');
/**
@@ -74,5 +76,6 @@ const pdfToImages = async (data: any): Promise => {
export {
toFile,
toPdfFile,
- toPdfFiles
+ toPdfFiles,
+ inPx
}
\ No newline at end of file
diff --git a/src/utils/zip.ts b/src/utils/zip.ts
index 71bc556..c289cbe 100644
--- a/src/utils/zip.ts
+++ b/src/utils/zip.ts
@@ -1,6 +1,6 @@
import JSZip from 'jszip'
import { toast } from 'react-toastify'
-import { OutputByType, OutputType } from '../types'
+import { InputFileFormat, OutputByType, OutputType } from '../types'
/**
* Read the content of a file within a zip archive.
@@ -9,7 +9,7 @@ import { OutputByType, OutputType } from '../types'
* @param outputType The type of output to return (e.g., 'string', 'arraybuffer', 'uint8array', etc.).
* @returns A Promise resolving to the content of the file, or null if an error occurs.
*/
-export const readContentOfZipEntry = async (
+const readContentOfZipEntry = async (
zip: JSZip,
filePath: string,
outputType: T
@@ -35,3 +35,20 @@ export const readContentOfZipEntry = async (
// Return the file content or null if an error occurred
return fileContent
}
+
+const loadZip = async (data: InputFileFormat): Promise => {
+ try {
+ return await JSZip.loadAsync(data);
+ } catch (err: any) {
+ console.log('err in loading zip file :>> ', err)
+ toast.error(err.message || 'An error occurred in loading zip file.')
+ return null;
+ }
+}
+
+export {
+ readContentOfZipEntry,
+ loadZip
+}
+
+