feat(pdf-marking): binds text to marks and saves with signatures
This commit is contained in:
parent
296b135c06
commit
4a932ffe03
@ -66,9 +66,11 @@
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.nonEditable {
|
||||
cursor: default;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.resizeHandle {
|
||||
|
@ -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}
|
||||
/>
|
||||
)
|
||||
}))
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
5
src/pages/sign/const.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { MarkType } from '../../types/drawing.ts'
|
||||
|
||||
export const MarkTypeTranslation: { [key: string]: string } = {
|
||||
[MarkType.FULLNAME.valueOf()]: "Full Name"
|
||||
}
|
@ -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
|
||||
files={files}
|
||||
marks={marks}
|
||||
fileHashes={currentFileHashes}
|
||||
handleMarkClick={handleMarkClick}
|
||||
{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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!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>
|
||||
|
@ -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"] {
|
||||
|
@ -30,6 +30,7 @@ export interface CreateSignatureEventContent {
|
||||
|
||||
export interface SignedEventContent {
|
||||
prevSig: string
|
||||
markConfig: Mark[]
|
||||
}
|
||||
|
||||
export interface Sigit {
|
||||
|
Loading…
Reference in New Issue
Block a user