PDF Markings #114
@ -59,14 +59,18 @@
|
|||||||
.drawingRectangle {
|
.drawingRectangle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 1px solid #01aaad;
|
border: 1px solid #01aaad;
|
||||||
width: 40px;
|
//width: 40px;
|
||||||
height: 40px;
|
//height: 40px;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
background-color: #01aaad4b;
|
background-color: #01aaad4b;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
&.nonEditable {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
.resizeHandle {
|
.resizeHandle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -5px;
|
right: -5px;
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import { PdfFile } from '../../types/drawing.ts'
|
import { PdfFile } from '../../types/drawing.ts'
|
||||||
import { MarkConfigDetails} from '../../types/mark.ts'
|
import { Mark, MarkConfigDetails } from '../../types/mark.ts'
|
||||||
import PdfPageItem from './PdfPageItem.tsx';
|
import PdfPageItem from './PdfPageItem.tsx';
|
||||||
|
|
||||||
interface PdfItemProps {
|
interface PdfItemProps {
|
||||||
pdfFile: PdfFile
|
pdfFile: PdfFile
|
||||||
markConfigDetails: MarkConfigDetails[]
|
marks: Mark[]
|
||||||
|
handleMarkClick: (id: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PdfItem = ({ pdfFile, markConfigDetails }: PdfItemProps) => {
|
const PdfItem = ({ pdfFile, marks, handleMarkClick }: PdfItemProps) => {
|
||||||
const filterMarkConfigDetails = (i: number) => {
|
const filterByPage = (marks: Mark[], page: number): Mark[] => {
|
||||||
return markConfigDetails.filter(
|
return marks.filter((mark) => mark.location.page === page);
|
||||||
(details) => details.markLocation.page === i);
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
pdfFile.pages.map((page, i) => {
|
pdfFile.pages.map((page, i) => {
|
||||||
console.log('page: ', page);
|
|
||||||
return (
|
return (
|
||||||
<PdfPageItem
|
<PdfPageItem
|
||||||
page={page}
|
page={page}
|
||||||
key={i}
|
key={i}
|
||||||
markConfigDetails={filterMarkConfigDetails(i)}
|
marks={filterByPage(marks, i)}
|
||||||
|
handleMarkClick={handleMarkClick}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
import { MarkLocation } from '../../types/mark.ts'
|
import { Mark, MarkLocation } from '../../types/mark.ts'
|
||||||
import styles from '../DrawPDFFields/style.module.scss'
|
import styles from '../DrawPDFFields/style.module.scss'
|
||||||
import { inPx } from '../../utils/pdf.ts'
|
import { inPx } from '../../utils/pdf.ts'
|
||||||
|
|
||||||
interface PdfMarkItemProps {
|
interface PdfMarkItemProps {
|
||||||
markLocation: MarkLocation
|
mark: Mark
|
||||||
|
handleMarkClick: (id: number) => void
|
||||||
|
isEditable: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const PdfMarkItem = ({ markLocation }: PdfMarkItemProps) => {
|
const PdfMarkItem = ({ mark, handleMarkClick, isEditable }: PdfMarkItemProps) => {
|
||||||
|
const handleClick = () => isEditable && handleMarkClick(mark.id);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.drawingRectangle}
|
onClick={handleClick}
|
||||||
|
className={`${styles.drawingRectangle} ${isEditable ? '' : styles.nonEditable}`}
|
||||||
style={{
|
style={{
|
||||||
left: inPx(markLocation.left),
|
left: inPx(mark.location.left),
|
||||||
top: inPx(markLocation.top),
|
top: inPx(mark.location.top),
|
||||||
width: inPx(markLocation.width),
|
width: inPx(mark.location.width),
|
||||||
height: inPx(markLocation.height)
|
height: inPx(mark.location.height)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -1,27 +1,45 @@
|
|||||||
import styles from '../DrawPDFFields/style.module.scss'
|
import styles from '../DrawPDFFields/style.module.scss'
|
||||||
import { PdfPage } from '../../types/drawing.ts'
|
import { PdfPage } from '../../types/drawing.ts'
|
||||||
import { MarkConfigDetails, MarkLocation } from '../../types/mark.ts'
|
import { Mark, MarkConfigDetails } from '../../types/mark.ts'
|
||||||
import PdfMarkItem from './PdfMarkItem.tsx'
|
import PdfMarkItem from './PdfMarkItem.tsx'
|
||||||
import { useState } from 'react';
|
import { useSelector } from 'react-redux'
|
||||||
|
import { State } from '../../store/rootReducer.ts'
|
||||||
|
import { hexToNpub } from '../../utils'
|
||||||
interface PdfPageProps {
|
interface PdfPageProps {
|
||||||
page: PdfPage
|
page: PdfPage
|
||||||
markConfigDetails: MarkConfigDetails[]
|
marks: Mark[]
|
||||||
|
handleMarkClick: (id: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PdfPageItem = ({ page, markConfigDetails }: PdfPageProps) => {
|
const PdfPageItem = ({ page, marks, handleMarkClick }: PdfPageProps) => {
|
||||||
const [currentMark, setCurrentMark] = useState<MarkLocation | null>(null);
|
const usersPubkey = useSelector((state: State) => state.auth.usersPubkey)
|
||||||
|
const isEditable = (mark: Mark): boolean => {
|
||||||
|
if (!usersPubkey) return false;
|
||||||
|
return mark.npub === hexToNpub(usersPubkey);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
className={styles.pdfImageWrapper}
|
||||||
style={{
|
style={{
|
||||||
border: '1px solid #c4c4c4',
|
border: '1px solid #c4c4c4',
|
||||||
marginBottom: '10px'
|
marginBottom: '10px',
|
||||||
|
marginTop: '10px'
|
||||||
}}
|
}}
|
||||||
className={styles.pdfImageWrapper}
|
|
||||||
>
|
>
|
||||||
<img draggable="false" style={{width: '100%'}} src={page.image} />
|
<img
|
||||||
{markConfigDetails.map((detail, i) => (
|
draggable="false"
|
||||||
<PdfMarkItem key={i} markLocation={detail.markLocation} />
|
src={page.image}
|
||||||
|
style={{ width: '100%'}}
|
||||||
|
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
marks.map((mark, i) => (
|
||||||
|
<PdfMarkItem
|
||||||
|
key={i}
|
||||||
|
mark={mark}
|
||||||
|
isEditable={isEditable(mark)}
|
||||||
|
handleMarkClick={handleMarkClick}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,41 +1,29 @@
|
|||||||
import { PdfFile } from '../../types/drawing.ts'
|
import { PdfFile } from '../../types/drawing.ts'
|
||||||
import { Box } from '@mui/material'
|
import { Box } from '@mui/material'
|
||||||
import PdfItem from './PdfItem.tsx'
|
import PdfItem from './PdfItem.tsx'
|
||||||
import { MarkConfig, MarkConfigDetails } from '../../types/mark.ts'
|
import { Mark, MarkConfigDetails } from '../../types/mark.ts'
|
||||||
import { State } from '../../store/rootReducer'
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { hexToNpub, npubToHex } from '../../utils'
|
|
||||||
|
|
||||||
interface PdfViewProps {
|
interface PdfViewProps {
|
||||||
files: { [filename: string]: PdfFile },
|
files: { [filename: string]: PdfFile },
|
||||||
fileHashes: { [key: string]: string | null },
|
fileHashes: { [key: string]: string | null },
|
||||||
markConfig: MarkConfig,
|
marks: Mark[],
|
||||||
|
handleMarkClick: (id: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PdfView = (props: PdfViewProps) => {
|
const PdfView = ({ files, fileHashes, marks, handleMarkClick }: PdfViewProps) => {
|
||||||
console.log('current file hashes: ', props.fileHashes)
|
const filterByFile = (marks: Mark[], fileHash: string): Mark[] => {
|
||||||
const usersPubkey = useSelector((state: State) => state.auth.usersPubkey);
|
return marks.filter((mark) => mark.pdfFileHash === fileHash);
|
||||||
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 (
|
return (
|
||||||
<Box>
|
<Box sx={{ width: '100%' }}>
|
||||||
{Object.entries(files)
|
{Object.entries(files)
|
||||||
.filter(([name]) => !!getMarkConfigDetails(name))
|
|
||||||
.map(([name, file], i) => (
|
.map(([name, file], i) => (
|
||||||
<PdfItem
|
<PdfItem
|
||||||
pdfFile={file}
|
pdfFile={file}
|
||||||
key={i}
|
key={i}
|
||||||
markConfigDetails={getMarkConfigDetails(name) as MarkConfigDetails[]} />
|
marks={filterByFile(marks, fileHashes[name] ?? "")}
|
||||||
|
handleMarkClick={handleMarkClick}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
16
src/components/PDFView/style.module.scss
Normal file
16
src/components/PDFView/style.module.scss
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
.imageWrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%; /* Adjust as needed */
|
||||||
|
height: 100%; /* Adjust as needed */
|
||||||
|
overflow: hidden; /* Ensure no overflow */
|
||||||
|
border: 1px solid #ccc; /* Optional: for visual debugging */
|
||||||
|
background-color: #e0f7fa; /* Optional: for visual debugging */
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain; /* Ensure the image fits within the container */
|
||||||
|
}
|
@ -62,6 +62,8 @@ import {
|
|||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
import { PdfFile } from '../../types/drawing'
|
import { PdfFile } from '../../types/drawing'
|
||||||
import { DrawPDFFields } from '../../components/DrawPDFFields'
|
import { DrawPDFFields } from '../../components/DrawPDFFields'
|
||||||
|
import { Mark } from '../../types/mark.ts'
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
export const CreatePage = () => {
|
export const CreatePage = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@ -339,34 +341,29 @@ export const CreatePage = () => {
|
|||||||
return fileHashes
|
return fileHashes
|
||||||
}
|
}
|
||||||
|
|
||||||
const createMarkConfig = (fileHashes: { [key: string]: string }) => {
|
const createMarkConfig = (fileHashes: { [key: string]: string }) : Mark[] => {
|
||||||
const markConfig: any = {}
|
return drawnPdfs.flatMap((drawnPdf) => {
|
||||||
|
const fileHash = fileHashes[drawnPdf.file.name];
|
||||||
drawnPdfs.forEach(drawnPdf => {
|
return drawnPdf.pages.flatMap((page, index) => {
|
||||||
const fileHash = fileHashes[drawnPdf.file.name]
|
return page.drawnFields.map((drawnField) => {
|
||||||
|
return {
|
||||||
drawnPdf.pages.forEach((page, pageIndex) => {
|
type: drawnField.type,
|
||||||
page.drawnFields.forEach(drawnField => {
|
location: {
|
||||||
if (!markConfig[drawnField.counterpart]) markConfig[drawnField.counterpart] = {}
|
page: index,
|
||||||
if (!markConfig[drawnField.counterpart][fileHash]) markConfig[drawnField.counterpart][fileHash] = []
|
|
||||||
|
|
||||||
console.log('drawn field: ', drawnField);
|
|
||||||
|
|
||||||
markConfig[drawnField.counterpart][fileHash].push({
|
|
||||||
markType: drawnField.type,
|
|
||||||
markLocation: {
|
|
||||||
page: pageIndex,
|
|
||||||
top: drawnField.top,
|
top: drawnField.top,
|
||||||
left: drawnField.left,
|
left: drawnField.left,
|
||||||
height: drawnField.height,
|
height: drawnField.height,
|
||||||
width: drawnField.width,
|
width: drawnField.width,
|
||||||
|
},
|
||||||
|
npub: drawnField.counterpart,
|
||||||
|
pdfFileHash: fileHash
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
.map((mark, index) => {
|
||||||
|
return {...mark, id: index }
|
||||||
return markConfig
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle errors during zip file generation
|
// Handle errors during zip file generation
|
||||||
|
32
src/pages/sign/MarkFormField.tsx
Normal file
32
src/pages/sign/MarkFormField.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { CurrentUserMark, Mark } from '../../types/mark.ts'
|
||||||
|
import styles from './style.module.scss'
|
||||||
|
import { Box, Button, TextField } from '@mui/material'
|
||||||
|
|
||||||
|
interface MarkFormFieldProps {
|
||||||
|
handleSubmit: (event: any) => void
|
||||||
|
handleChange: (event: any) => void
|
||||||
|
currentMark: CurrentUserMark
|
||||||
|
currentMarkValue: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const MarkFormField = (props: MarkFormFieldProps) => {
|
||||||
|
const { handleSubmit, handleChange, currentMark, currentMarkValue } = props;
|
||||||
|
const getSubmitButton = () => currentMark.isLast ? 'Complete' : 'Next';
|
||||||
|
return (
|
||||||
|
<div className={styles.fixedBottomForm}>
|
||||||
|
<Box component="form" onSubmit={handleSubmit}>
|
||||||
|
<TextField
|
||||||
|
id="mark-value"
|
||||||
|
label={currentMark.mark.type}
|
||||||
|
value={currentMarkValue}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<Button type="submit" variant="contained">
|
||||||
|
{getSubmitButton()}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MarkFormField;
|
@ -1,8 +1,8 @@
|
|||||||
import { Box, Button, Typography } from '@mui/material'
|
import { Box, Button, FormControl, InputLabel, TextField, Typography } from '@mui/material'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import saveAs from 'file-saver'
|
import saveAs from 'file-saver'
|
||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import _ from 'lodash'
|
import _, { set } from 'lodash'
|
||||||
import { MuiFileInput } from 'mui-file-input'
|
import { MuiFileInput } from 'mui-file-input'
|
||||||
import { Event, verifyEvent } from 'nostr-tools'
|
import { Event, verifyEvent } from 'nostr-tools'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
@ -36,7 +36,8 @@ import styles from './style.module.scss'
|
|||||||
import { PdfFile } from '../../types/drawing.ts'
|
import { PdfFile } from '../../types/drawing.ts'
|
||||||
import { toFile, toPdfFile } from '../../utils/pdf.ts'
|
import { toFile, toPdfFile } from '../../utils/pdf.ts'
|
||||||
import PdfView from '../../components/PDFView'
|
import PdfView from '../../components/PDFView'
|
||||||
import { MarkConfig } from '../../types/mark.ts'
|
import { CurrentUserMark, Mark, MarkConfig, MarkConfigDetails, User } from '../../types/mark.ts'
|
||||||
|
import MarkFormField from './MarkFormField.tsx'
|
||||||
enum SignedStatus {
|
enum SignedStatus {
|
||||||
Fully_Signed,
|
Fully_Signed,
|
||||||
User_Is_Next_Signer,
|
User_Is_Next_Signer,
|
||||||
@ -74,7 +75,7 @@ export const SignPage = () => {
|
|||||||
|
|
||||||
const [signers, setSigners] = useState<`npub1${string}`[]>([])
|
const [signers, setSigners] = useState<`npub1${string}`[]>([])
|
||||||
const [viewers, setViewers] = useState<`npub1${string}`[]>([])
|
const [viewers, setViewers] = useState<`npub1${string}`[]>([])
|
||||||
const [markConfig, setMarkConfig] = useState<MarkConfig | null>(null)
|
const [marks, setMarks] = useState<Mark[] >([])
|
||||||
const [creatorFileHashes, setCreatorFileHashes] = useState<{
|
const [creatorFileHashes, setCreatorFileHashes] = useState<{
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
}>({})
|
}>({})
|
||||||
@ -93,6 +94,11 @@ export const SignPage = () => {
|
|||||||
|
|
||||||
const [authUrl, setAuthUrl] = useState<string>()
|
const [authUrl, setAuthUrl] = useState<string>()
|
||||||
const nostrController = NostrController.getInstance()
|
const nostrController = NostrController.getInstance()
|
||||||
|
const [currentUserMark, setCurrentUserMark] = useState<CurrentUserMark | null>(null);
|
||||||
|
const [currentUserMarks, setCurrentUserMarks] = useState<CurrentUserMark[]>([]);
|
||||||
|
const [currentMarkValue, setCurrentMarkValue] = useState<string>('');
|
||||||
|
const [isMarksCompleted, setIsMarksCompleted] = useState(false);
|
||||||
|
const [isLastUserMark, setIsLastUserMark] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (signers.length > 0) {
|
if (signers.length > 0) {
|
||||||
@ -183,9 +189,25 @@ export const SignPage = () => {
|
|||||||
setViewers(createSignatureContent.viewers)
|
setViewers(createSignatureContent.viewers)
|
||||||
setCreatorFileHashes(createSignatureContent.fileHashes)
|
setCreatorFileHashes(createSignatureContent.fileHashes)
|
||||||
setSubmittedBy(createSignatureEvent.pubkey)
|
setSubmittedBy(createSignatureEvent.pubkey)
|
||||||
setMarkConfig(createSignatureContent.markConfig);
|
setMarks(createSignatureContent.markConfig);
|
||||||
|
|
||||||
console.log('createSignatureContent', createSignatureContent)
|
console.log('createSignatureContent markConfig', createSignatureContent);
|
||||||
|
if (usersPubkey) {
|
||||||
|
console.log('this runs behind users pubkey');
|
||||||
|
const curMarks = getCurrentUserMarks(createSignatureContent.markConfig, usersPubkey)
|
||||||
|
if (curMarks.length === 0) {
|
||||||
|
setIsMarksCompleted(true)
|
||||||
|
} else {
|
||||||
|
const nextMark = findNextIncompleteMark(curMarks)
|
||||||
|
if (!nextMark) {
|
||||||
|
setIsMarksCompleted(true)
|
||||||
|
} else {
|
||||||
|
setCurrentUserMark(nextMark)
|
||||||
|
setIsMarksCompleted(false)
|
||||||
|
}
|
||||||
|
setCurrentUserMarks(curMarks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setSignedBy(Object.keys(meta.docSignatures) as `npub1${string}`[])
|
setSignedBy(Object.keys(meta.docSignatures) as `npub1${string}`[])
|
||||||
}
|
}
|
||||||
@ -514,6 +536,57 @@ export const SignPage = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMarkClick = (id: number) => {
|
||||||
|
const nextMark = currentUserMarks.find(mark => mark.mark.id === id)
|
||||||
|
setCurrentUserMark(nextMark!)
|
||||||
|
setCurrentMarkValue(nextMark?.mark.value || "")
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMarkConfigPerUser = (markConfig: MarkConfig) => {
|
||||||
|
if (!usersPubkey) return;
|
||||||
|
return markConfig[hexToNpub(usersPubkey)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = (event: any) => setCurrentMarkValue(event.target.value);
|
||||||
|
|
||||||
|
const handleSubmit = (event: any) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!currentMarkValue || !currentUserMark) return;
|
||||||
|
|
||||||
|
const curMark = {
|
||||||
|
...currentUserMark.mark,
|
||||||
|
value: currentMarkValue
|
||||||
|
};
|
||||||
|
|
||||||
|
const indexToUpdate = marks.findIndex(mark => mark.id === curMark.id);
|
||||||
|
|
||||||
|
const updatedMarks: Mark[] = [
|
||||||
|
...marks.slice(0, indexToUpdate),
|
||||||
|
curMark,
|
||||||
|
...marks.slice(indexToUpdate + 1)
|
||||||
|
];
|
||||||
|
|
||||||
|
setMarks(updatedMarks)
|
||||||
|
setCurrentMarkValue("")
|
||||||
|
|
||||||
|
const updatedCurUserMarks = getCurrentUserMarks(updatedMarks, usersPubkey!)
|
||||||
|
console.log('updatedCurUserMarks: ', updatedCurUserMarks)
|
||||||
|
setCurrentUserMarks(updatedCurUserMarks)
|
||||||
|
const nextMark = findNextIncompleteMark(updatedCurUserMarks)
|
||||||
|
console.log('next mark: ', nextMark)
|
||||||
|
if (!nextMark) {
|
||||||
|
setCurrentUserMark(null)
|
||||||
|
setIsMarksCompleted(true)
|
||||||
|
} else {
|
||||||
|
setCurrentUserMark(nextMark)
|
||||||
|
setIsMarksCompleted(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const findNextIncompleteMark = (usersMarks: CurrentUserMark[]): CurrentUserMark | undefined => {
|
||||||
|
return usersMarks.find(mark => !mark.isCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
// Update the meta signatures
|
// Update the meta signatures
|
||||||
const updateMetaSignatures = (meta: Meta, signedEvent: SignedEvent): Meta => {
|
const updateMetaSignatures = (meta: Meta, signedEvent: SignedEvent): Meta => {
|
||||||
const metaCopy = _.cloneDeep(meta)
|
const metaCopy = _.cloneDeep(meta)
|
||||||
@ -735,6 +808,25 @@ export const SignPage = () => {
|
|||||||
navigate(appPublicRoutes.verify)
|
navigate(appPublicRoutes.verify)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getCurrentUserMarks = (marks: Mark[], pubkey: string): CurrentUserMark[] => {
|
||||||
|
console.log('marks: ', marks);
|
||||||
|
|
||||||
|
const filteredMarks = marks
|
||||||
|
.filter(mark => mark.npub === hexToNpub(pubkey))
|
||||||
|
const currentMarks = filteredMarks
|
||||||
|
.map((mark, index, arr) => {
|
||||||
|
return {
|
||||||
|
mark,
|
||||||
|
isLast: isLast(index, arr),
|
||||||
|
isCompleted: !!mark.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log('current marks: ', currentMarks)
|
||||||
|
return currentMarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLast = (index: number, arr: any[]) => (index === (arr.length -1))
|
||||||
|
|
||||||
const handleExportSigit = async () => {
|
const handleExportSigit = async () => {
|
||||||
if (Object.entries(files).length === 0 || !meta) return
|
if (Object.entries(files).length === 0 || !meta) return
|
||||||
|
|
||||||
@ -852,9 +944,12 @@ export const SignPage = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <LoadingSpinner desc={loadingSpinnerDesc} />
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
|
|
||||||
<Box className={styles.container}>
|
<Box className={styles.container}>
|
||||||
{displayInput && (
|
{displayInput && (
|
||||||
<>
|
<>
|
||||||
@ -881,56 +976,68 @@ export const SignPage = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{submittedBy && Object.entries(files).length > 0 && meta && (
|
{/*{submittedBy && Object.entries(files).length > 0 && meta && (*/}
|
||||||
<>
|
{/* <>*/}
|
||||||
<DisplayMeta
|
{/* <DisplayMeta*/}
|
||||||
meta={meta}
|
{/* 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}
|
files={files}
|
||||||
submittedBy={submittedBy}
|
marks={marks}
|
||||||
signers={signers}
|
fileHashes={currentFileHashes}
|
||||||
viewers={viewers}
|
handleMarkClick={handleMarkClick}
|
||||||
creatorFileHashes={creatorFileHashes}
|
|
||||||
currentFileHashes={currentFileHashes}
|
|
||||||
signedBy={signedBy}
|
|
||||||
nextSigner={nextSinger}
|
|
||||||
getPrevSignersSig={getPrevSignersSig}
|
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{signedStatus === SignedStatus.Fully_Signed && (
|
{
|
||||||
<Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
|
!isMarksCompleted && currentUserMark !== null && <MarkFormField
|
||||||
<Button onClick={handleExport} variant="contained">
|
handleSubmit={handleSubmit}
|
||||||
Export
|
handleChange={handleChange}
|
||||||
</Button>
|
currentMark={currentUserMark}
|
||||||
</Box>
|
currentMarkValue={currentMarkValue}
|
||||||
)}
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
{signedStatus === SignedStatus.User_Is_Next_Signer && (
|
{ isMarksCompleted && <p>Ready to Sign!</p>}
|
||||||
<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>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{markConfig && (<PdfView
|
|
||||||
files={files}
|
|
||||||
markConfig={markConfig}
|
|
||||||
fileHashes={currentFileHashes}/>)}
|
|
||||||
|
|
||||||
<div
|
|
||||||
className={styles.fixedBottomForm}>
|
|
||||||
<input type="text" placeholder="type here..." />
|
|
||||||
<button>Next</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
|
@ -51,7 +51,10 @@
|
|||||||
.fixedBottomForm {
|
.fixedBottomForm {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 50%;
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1);
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { MarkConfig } from "./mark"
|
import { Mark } from './mark'
|
||||||
import { Keys } from '../store/auth/types'
|
import { Keys } from '../store/auth/types'
|
||||||
|
|
||||||
export enum UserRole {
|
export enum UserRole {
|
||||||
@ -23,7 +23,7 @@ export interface CreateSignatureEventContent {
|
|||||||
signers: `npub1${string}`[]
|
signers: `npub1${string}`[]
|
||||||
viewers: `npub1${string}`[]
|
viewers: `npub1${string}`[]
|
||||||
fileHashes: { [key: string]: string }
|
fileHashes: { [key: string]: string }
|
||||||
markConfig: MarkConfig
|
markConfig: Mark[]
|
||||||
title: string
|
title: string
|
||||||
zipUrl: string
|
zipUrl: string
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,25 @@
|
|||||||
import { MarkType } from "./drawing";
|
import { MarkType } from "./drawing";
|
||||||
|
|
||||||
|
// export interface Mark {
|
||||||
|
// /**
|
||||||
|
// * @key png (pdf page) file hash
|
||||||
|
// */
|
||||||
|
// [key: string]: MarkConfigDetails[]
|
||||||
|
// }
|
||||||
|
|
||||||
|
export interface CurrentUserMark {
|
||||||
|
mark: Mark
|
||||||
|
isLast: boolean
|
||||||
|
isCompleted: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface Mark {
|
export interface Mark {
|
||||||
/**
|
id: number;
|
||||||
* @key png (pdf page) file hash
|
npub: string;
|
||||||
*/
|
pdfFileHash: string;
|
||||||
[key: string]: MarkConfigDetails[]
|
type: MarkType;
|
||||||
|
location: MarkLocation;
|
||||||
|
value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkConfig {
|
export interface MarkConfig {
|
||||||
@ -33,11 +48,12 @@ export interface MarkValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkConfigDetails {
|
export interface MarkConfigDetails {
|
||||||
markType: MarkType;
|
type: MarkType;
|
||||||
/**
|
/**
|
||||||
* Coordinates in format: X:10;Y:50
|
* Coordinates in format: X:10;Y:50
|
||||||
*/
|
*/
|
||||||
markLocation: MarkLocation;
|
location: MarkLocation;
|
||||||
|
value?: MarkValue
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkLocation {
|
export interface MarkLocation {
|
||||||
|
Loading…
Reference in New Issue
Block a user