feat(pdf-marking): binds text to marks and saves with signatures

This commit is contained in:
eugene 2024-07-23 12:22:53 +03:00
parent 296b135c06
commit 4a932ffe03
10 changed files with 122 additions and 73 deletions

View File

@ -66,9 +66,11 @@
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
&.nonEditable {
cursor: default;
visibility: hidden;
}
.resizeHandle {

View File

@ -1,14 +1,16 @@
import { PdfFile } from '../../types/drawing.ts'
import { Mark, MarkConfigDetails } from '../../types/mark.ts'
import { CurrentUserMark, Mark, MarkConfigDetails } from '../../types/mark.ts'
import PdfPageItem from './PdfPageItem.tsx';
interface PdfItemProps {
pdfFile: PdfFile
marks: Mark[]
handleMarkClick: (id: number) => void
currentMarkValue: string
currentUserMark: CurrentUserMark | null
}
const PdfItem = ({ pdfFile, marks, handleMarkClick }: PdfItemProps) => {
const PdfItem = ({ pdfFile, marks, handleMarkClick, currentMarkValue, currentUserMark }: PdfItemProps) => {
const filterByPage = (marks: Mark[], page: number): Mark[] => {
return marks.filter((mark) => mark.location.page === page);
}
@ -20,6 +22,8 @@ const PdfItem = ({ pdfFile, marks, handleMarkClick }: PdfItemProps) => {
key={i}
marks={filterByPage(marks, i)}
handleMarkClick={handleMarkClick}
currentMarkValue={currentMarkValue}
currentUserMark={currentUserMark}
/>
)
}))

View File

@ -1,4 +1,4 @@
import { Mark, MarkLocation } from '../../types/mark.ts'
import { CurrentUserMark, Mark, MarkLocation } from '../../types/mark.ts'
import styles from '../DrawPDFFields/style.module.scss'
import { inPx } from '../../utils/pdf.ts'
@ -6,10 +6,17 @@ interface PdfMarkItemProps {
mark: Mark
handleMarkClick: (id: number) => void
isEditable: boolean
currentMarkValue: string
currentUserMark: CurrentUserMark | null
}
const PdfMarkItem = ({ mark, handleMarkClick, isEditable }: PdfMarkItemProps) => {
const PdfMarkItem = ({ mark, handleMarkClick, isEditable, currentMarkValue, currentUserMark }: PdfMarkItemProps) => {
const handleClick = () => isEditable && handleMarkClick(mark.id);
const getMarkValue = () => (
currentUserMark?.mark.id === mark.id
? currentMarkValue
: mark.value
)
return (
<div
onClick={handleClick}
@ -20,7 +27,7 @@ const PdfMarkItem = ({ mark, handleMarkClick, isEditable }: PdfMarkItemProps) =>
width: inPx(mark.location.width),
height: inPx(mark.location.height)
}}
/>
>{getMarkValue()}</div>
)
}

View File

@ -1,6 +1,6 @@
import styles from '../DrawPDFFields/style.module.scss'
import { PdfPage } from '../../types/drawing.ts'
import { Mark, MarkConfigDetails } from '../../types/mark.ts'
import { CurrentUserMark, Mark, MarkConfigDetails } from '../../types/mark.ts'
import PdfMarkItem from './PdfMarkItem.tsx'
import { useSelector } from 'react-redux'
import { State } from '../../store/rootReducer.ts'
@ -9,9 +9,11 @@ interface PdfPageProps {
page: PdfPage
marks: Mark[]
handleMarkClick: (id: number) => void
currentMarkValue: string
currentUserMark: CurrentUserMark | null
}
const PdfPageItem = ({ page, marks, handleMarkClick }: PdfPageProps) => {
const PdfPageItem = ({ page, marks, handleMarkClick, currentMarkValue, currentUserMark }: PdfPageProps) => {
const usersPubkey = useSelector((state: State) => state.auth.usersPubkey)
const isEditable = (mark: Mark): boolean => {
if (!usersPubkey) return false;
@ -39,6 +41,8 @@ const PdfPageItem = ({ page, marks, handleMarkClick }: PdfPageProps) => {
mark={mark}
isEditable={isEditable(mark)}
handleMarkClick={handleMarkClick}
currentMarkValue={currentMarkValue}
currentUserMark={currentUserMark}
/>
))}
</div>

View File

@ -1,16 +1,18 @@
import { PdfFile } from '../../types/drawing.ts'
import { Box } from '@mui/material'
import PdfItem from './PdfItem.tsx'
import { Mark, MarkConfigDetails } from '../../types/mark.ts'
import { CurrentUserMark, Mark, MarkConfigDetails } from '../../types/mark.ts'
interface PdfViewProps {
files: { [filename: string]: PdfFile },
fileHashes: { [key: string]: string | null },
marks: Mark[],
handleMarkClick: (id: number) => void
currentMarkValue: string
currentUserMark: CurrentUserMark | null
}
const PdfView = ({ files, fileHashes, marks, handleMarkClick }: PdfViewProps) => {
const PdfView = ({ files, fileHashes, marks, handleMarkClick, currentMarkValue, currentUserMark }: PdfViewProps) => {
const filterByFile = (marks: Mark[], fileHash: string): Mark[] => {
return marks.filter((mark) => mark.pdfFileHash === fileHash);
}
@ -23,6 +25,8 @@ const PdfView = ({ files, fileHashes, marks, handleMarkClick }: PdfViewProps) =>
key={i}
marks={filterByFile(marks, fileHashes[name] ?? "")}
handleMarkClick={handleMarkClick}
currentMarkValue={currentMarkValue}
currentUserMark={currentUserMark}
/>
))}
</Box>

View File

@ -1,6 +1,7 @@
import { CurrentUserMark, Mark } from '../../types/mark.ts'
import styles from './style.module.scss'
import { Box, Button, TextField } from '@mui/material'
import { MarkTypeTranslation } from './const.ts'
interface MarkFormFieldProps {
handleSubmit: (event: any) => void
@ -17,7 +18,7 @@ const MarkFormField = (props: MarkFormFieldProps) => {
<Box component="form" onSubmit={handleSubmit}>
<TextField
id="mark-value"
label={currentMark.mark.type}
label={MarkTypeTranslation[currentMark.mark.type.valueOf()]}
value={currentMarkValue}
onChange={handleChange}
/>

5
src/pages/sign/const.ts Normal file
View File

@ -0,0 +1,5 @@
import { MarkType } from '../../types/drawing.ts'
export const MarkTypeTranslation: { [key: string]: string } = {
[MarkType.FULLNAME.valueOf()]: "Full Name"
}

View File

@ -13,7 +13,7 @@ import { LoadingSpinner } from '../../components/LoadingSpinner'
import { NostrController } from '../../controllers'
import { appPublicRoutes } from '../../routes'
import { State } from '../../store/rootReducer'
import { CreateSignatureEventContent, Meta, SignedEvent } from '../../types'
import { CreateSignatureEventContent, Meta, SignedEvent, UserRole } from '../../types'
import {
decryptArrayBuffer,
encryptArrayBuffer,
@ -239,6 +239,7 @@ export const SignPage = () => {
.then((res) => {
handleArrayBufferFromBlossom(res.data, encryptionKey)
setMeta(metaInNavState)
console.log('meta in nav state: ', metaInNavState)
})
.catch((err) => {
console.error(`error occurred in getting file from ${zipUrl}`, err)
@ -490,6 +491,8 @@ export const SignPage = () => {
}
)
console.log('parsed meta: ', parsedMetaJson)
setMeta(parsedMetaJson)
}
@ -514,7 +517,10 @@ export const SignPage = () => {
const prevSig = getPrevSignersSig(hexToNpub(usersPubkey!))
if (!prevSig) return
const signedEvent = await signEventForMeta(prevSig)
const marks = getSignerMarksForMeta()
if (!marks) return
const signedEvent = await signEventForMeta({ prevSig, marks })
if (!signedEvent) return
const updatedMeta = updateMetaSignatures(meta, signedEvent)
@ -528,14 +534,19 @@ export const SignPage = () => {
}
// Sign the event for the meta file
const signEventForMeta = async (prevSig: string) => {
const signEventForMeta = async (signerContent: { prevSig: string, marks: Mark[] }) => {
return await signEventForMetaFile(
JSON.stringify({ prevSig }),
JSON.stringify(signerContent),
nostrController,
setIsLoading
)
}
const getSignerMarksForMeta = (): Mark[] | undefined => {
if (currentUserMarks.length === 0) return;
return currentUserMarks.map(( { mark }: CurrentUserMark) => mark);
}
const handleMarkClick = (id: number) => {
const nextMark = currentUserMarks.find(mark => mark.mark.id === id)
setCurrentUserMark(nextMark!)
@ -595,6 +606,7 @@ export const SignPage = () => {
[hexToNpub(signedEvent.pubkey)]: JSON.stringify(signedEvent, null, 2)
}
metaCopy.modifiedAt = now()
console.log('meta copy: ', metaCopy);
return metaCopy
}
@ -719,6 +731,7 @@ export const SignPage = () => {
await Promise.all(promises)
.then(() => {
toast.success('Notifications sent successfully')
console.log('meta: ', meta);
setMeta(meta)
})
.catch(() => {
@ -948,6 +961,34 @@ export const SignPage = () => {
return <LoadingSpinner desc={loadingSpinnerDesc} />
}
if (!isMarksCompleted) {
return (
<>
<Box className={styles.container}>
{
marks.length > 0 && (
<PdfView
files={files}
marks={marks}
fileHashes={currentFileHashes}
handleMarkClick={handleMarkClick}
currentMarkValue={currentMarkValue}
currentUserMark={currentUserMark}
/>)}
{
currentUserMark !== null && (
<MarkFormField
handleSubmit={handleSubmit}
handleChange={handleChange}
currentMark={currentUserMark}
currentMarkValue={currentMarkValue}
/>
)}
</Box>
</>
)}
return (
<>
<Box className={styles.container}>
@ -976,67 +1017,46 @@ export const SignPage = () => {
</>
)}
{/*{submittedBy && Object.entries(files).length > 0 && meta && (*/}
{/* <>*/}
{/* <DisplayMeta*/}
{/* meta={meta}*/}
{/* files={files}*/}
{/* submittedBy={submittedBy}*/}
{/* signers={signers}*/}
{/* viewers={viewers}*/}
{/* creatorFileHashes={creatorFileHashes}*/}
{/* currentFileHashes={currentFileHashes}*/}
{/* signedBy={signedBy}*/}
{/* nextSigner={nextSinger}*/}
{/* getPrevSignersSig={getPrevSignersSig}*/}
{/* />*/}
{/* {signedStatus === SignedStatus.Fully_Signed && (*/}
{/* <Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>*/}
{/* <Button onClick={handleExport} variant="contained">*/}
{/* Export*/}
{/* </Button>*/}
{/* </Box>*/}
{/* )}*/}
{/* {signedStatus === SignedStatus.User_Is_Next_Signer && (*/}
{/* <Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>*/}
{/* <Button onClick={handleSign} variant="contained">*/}
{/* Sign*/}
{/* </Button>*/}
{/* </Box>*/}
{/* )}*/}
{/* {isSignerOrCreator && (*/}
{/* <Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>*/}
{/* <Button onClick={handleExportSigit} variant="contained">*/}
{/* Export Sigit*/}
{/* </Button>*/}
{/* </Box>*/}
{/* )}*/}
{/* </>*/}
{/*)}*/}
{
!isMarksCompleted && marks.length > 0 && (
<PdfView
{submittedBy && Object.entries(files).length > 0 && meta && (
<>
<DisplayMeta
meta={meta}
files={files}
marks={marks}
fileHashes={currentFileHashes}
handleMarkClick={handleMarkClick}
submittedBy={submittedBy}
signers={signers}
viewers={viewers}
creatorFileHashes={creatorFileHashes}
currentFileHashes={currentFileHashes}
signedBy={signedBy}
nextSigner={nextSinger}
getPrevSignersSig={getPrevSignersSig}
/>
)
}
{
!isMarksCompleted && currentUserMark !== null && <MarkFormField
handleSubmit={handleSubmit}
handleChange={handleChange}
currentMark={currentUserMark}
currentMarkValue={currentMarkValue}
/>
}
{signedStatus === SignedStatus.Fully_Signed && (
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
<Button onClick={handleExport} variant="contained">
Export
</Button>
</Box>
)}
{ isMarksCompleted && <p>Ready to Sign!</p>}
{signedStatus === SignedStatus.User_Is_Next_Signer && (
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
<Button onClick={handleSign} variant="contained">
Sign
</Button>
</Box>
)}
{isSignerOrCreator && (
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
<Button onClick={handleExportSigit} variant="contained">
Export Sigit
</Button>
</Box>
)}
</>
)}
</Box>

View File

@ -55,13 +55,14 @@
transform: translateX(-50%);
width: 100%;
max-width: 500px;
height: 100px;
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;
//z-index: 200;
}
.fixedBottomForm input[type="text"] {

View File

@ -30,6 +30,7 @@ export interface CreateSignatureEventContent {
export interface SignedEventContent {
prevSig: string
markConfig: Mark[]
}
export interface Sigit {