CI - Add git hooks and staging checks #128

Merged
enes merged 13 commits from issue-38-90-pipeline-check into staging 2024-08-07 13:54:35 +00:00
20 changed files with 523 additions and 374 deletions
Showing only changes of commit e3eb1f37c1 - Show all commits

View File

@ -1,15 +1,43 @@
import { AccessTime, CalendarMonth, ExpandMore, Gesture, PictureAsPdf, Badge, Work, Close } from '@mui/icons-material'
import { Box, Typography, Accordion, AccordionDetails, AccordionSummary, CircularProgress, FormControl, InputLabel, MenuItem, Select } from '@mui/material'
import {
AccessTime,
CalendarMonth,
ExpandMore,
Gesture,
PictureAsPdf,
Badge,
Work,
Close
} from '@mui/icons-material'
import {
Box,
Typography,
Accordion,
AccordionDetails,
AccordionSummary,
CircularProgress,
FormControl,
InputLabel,
MenuItem,
Select
} from '@mui/material'
import styles from './style.module.scss'
import { useEffect, useState } from 'react'
import * as PDFJS from "pdfjs-dist";
import { ProfileMetadata, User } from '../../types';
import { PdfFile, DrawTool, MouseState, PdfPage, DrawnField, MarkType } from '../../types/drawing';
import { truncate } from 'lodash';
import { hexToNpub } from '../../utils';
import * as PDFJS from 'pdfjs-dist'
import { ProfileMetadata, User } from '../../types'
import {
PdfFile,
DrawTool,
MouseState,
PdfPage,
DrawnField,
MarkType
} from '../../types/drawing'
import { truncate } from 'lodash'
import { hexToNpub } from '../../utils'
import { toPdfFiles } from '../../utils/pdf.ts'
PDFJS.GlobalWorkerOptions.workerSrc = 'node_modules/pdfjs-dist/build/pdf.worker.mjs';
PDFJS.GlobalWorkerOptions.workerSrc =
'node_modules/pdfjs-dist/build/pdf.worker.mjs'
interface Props {
selectedFiles: File[]
@ -20,44 +48,43 @@ interface Props {
export const DrawPDFFields = (props: Props) => {
const { selectedFiles } = props
const [pdfFiles, setPdfFiles] = useState<PdfFile[]>([])
const [parsingPdf, setParsingPdf] = useState<boolean>(false)
const [showDrawToolBox, setShowDrawToolBox] = useState<boolean>(false)
const [selectedTool, setSelectedTool] = useState<DrawTool | null>()
const [toolbox] = useState<DrawTool[]>([
{
identifier: MarkType.SIGNATURE,
icon: <Gesture/>,
icon: <Gesture />,
label: 'Signature',
active: false
},
{
identifier: MarkType.FULLNAME,
icon: <Badge/>,
icon: <Badge />,
label: 'Full Name',
active: true
},
{
identifier: MarkType.JOBTITLE,
icon: <Work/>,
icon: <Work />,
label: 'Job Title',
active: false
},
{
identifier: MarkType.DATE,
icon: <CalendarMonth/>,
icon: <CalendarMonth />,
label: 'Date',
active: false
},
{
identifier: MarkType.DATETIME,
icon: <AccessTime/>,
icon: <AccessTime />,
label: 'Datetime',
active: false
},
}
])
const [mouseState, setMouseState] = useState<MouseState>({
@ -67,7 +94,7 @@ export const DrawPDFFields = (props: Props) => {
useEffect(() => {
if (selectedFiles) {
setParsingPdf(true)
parsePdfPages().finally(() => {
setParsingPdf(false)
})
@ -81,13 +108,13 @@ export const DrawPDFFields = (props: Props) => {
/**
* Drawing events
*/
useEffect(() => {
useEffect(() => {
// window.addEventListener('mousedown', onMouseDown);
window.addEventListener('mouseup', onMouseUp);
window.addEventListener('mouseup', onMouseUp)
return () => {
// window.removeEventListener('mousedown', onMouseDown);
window.removeEventListener('mouseup', onMouseUp);
window.removeEventListener('mouseup', onMouseUp)
}
}, [])
@ -106,7 +133,7 @@ export const DrawPDFFields = (props: Props) => {
const onMouseDown = (event: any, page: PdfPage) => {
// Proceed only if left click
if (event.button !== 0) return
// Only allow drawing if mouse is not over other drawn element
const isOverPdfImageWrapper = event.target.tagName === 'IMG'
@ -158,11 +185,11 @@ export const DrawPDFFields = (props: Props) => {
*/
const onMouseMove = (event: any, page: PdfPage) => {
if (mouseState.clicked && selectedTool) {
const lastElementIndex = page.drawnFields.length -1
const lastElementIndex = page.drawnFields.length - 1
const lastDrawnField = page.drawnFields[lastElementIndex]
const { mouseX, mouseY } = getMouseCoordinates(event)
const width = mouseX - lastDrawnField.left
const height = mouseY - lastDrawnField.top
@ -172,10 +199,10 @@ export const DrawPDFFields = (props: Props) => {
const currentDrawnFields = page.drawnFields
currentDrawnFields[lastElementIndex] = lastDrawnField
refreshPdfFiles()
}
}
}
/**
* Fired when event happens on the drawn element which will be moved
@ -189,7 +216,7 @@ export const DrawPDFFields = (props: Props) => {
*/
const onDrawnFieldMouseDown = (event: any) => {
event.stopPropagation()
// Proceed only if left click
if (event.button !== 0) return
@ -212,7 +239,10 @@ export const DrawPDFFields = (props: Props) => {
*/
const onDranwFieldMouseMove = (event: any, drawnField: DrawnField) => {
if (mouseState.dragging) {
const { mouseX, mouseY, rect } = getMouseCoordinates(event, event.target.parentNode)
const { mouseX, mouseY, rect } = getMouseCoordinates(
event,
event.target.parentNode
)
const coordsOffset = mouseState.coordsInWrapper
if (coordsOffset) {
@ -258,8 +288,11 @@ export const DrawPDFFields = (props: Props) => {
*/
const onResizeHandleMouseMove = (event: any, drawnField: DrawnField) => {
if (mouseState.resizing) {
const { mouseX, mouseY } = getMouseCoordinates(event, event.target.parentNode.parentNode)
const { mouseX, mouseY } = getMouseCoordinates(
event,
event.target.parentNode.parentNode
)
const width = mouseX - drawnField.left
const height = mouseY - drawnField.top
@ -277,10 +310,18 @@ export const DrawPDFFields = (props: Props) => {
* @param pdfPageIndex pdf page index
* @param drawnFileIndex drawn file index
*/
const onRemoveHandleMouseDown = (event: any, pdfFileIndex: number, pdfPageIndex: number, drawnFileIndex: number) => {
const onRemoveHandleMouseDown = (
event: any,
pdfFileIndex: number,
pdfPageIndex: number,
drawnFileIndex: number
) => {
event.stopPropagation()
pdfFiles[pdfFileIndex].pages[pdfPageIndex].drawnFields.splice(drawnFileIndex, 1)
pdfFiles[pdfFileIndex].pages[pdfPageIndex].drawnFields.splice(
drawnFileIndex,
1
)
}
/**
@ -300,9 +341,9 @@ export const DrawPDFFields = (props: Props) => {
*/
const getMouseCoordinates = (event: any, customTarget?: any) => {
const target = customTarget ? customTarget : event.target
const rect = target.getBoundingClientRect();
const mouseX = event.clientX - rect.left; //x position within the element.
const mouseY = event.clientY - rect.top; //y position within the element.
const rect = target.getBoundingClientRect()
const mouseX = event.clientX - rect.left //x position within the element.
const mouseY = event.clientY - rect.top //y position within the element.
return {
mouseX,
@ -316,8 +357,8 @@ export const DrawPDFFields = (props: Props) => {
* creates the pdfFiles object and sets to a state
*/
const parsePdfPages = async () => {
const pdfFiles: PdfFile[] = await toPdfFiles(selectedFiles);
const pdfFiles: PdfFile[] = await toPdfFiles(selectedFiles)
setPdfFiles(pdfFiles)
}
@ -326,7 +367,7 @@ export const DrawPDFFields = (props: Props) => {
* @returns if expanded pdf accordion is present
*/
const hasExpandedPdf = () => {
return !!pdfFiles.filter(pdfFile => !!pdfFile.expanded).length
return !!pdfFiles.filter((pdfFile) => !!pdfFile.expanded).length
}
const handleAccordionExpandChange = (expanded: boolean, pdfFile: PdfFile) => {
@ -355,9 +396,11 @@ export const DrawPDFFields = (props: Props) => {
*/
const getPdfPages = (pdfFile: PdfFile, pdfFileIndex: number) => {
return (
<Box sx={{
width: '100%'
}}>
<Box
sx={{
width: '100%'
}}
>
{pdfFile.pages.map((page, pdfPageIndex: number) => {
return (
<div
@ -367,17 +410,27 @@ export const DrawPDFFields = (props: Props) => {
marginBottom: '10px'
}}
className={`${styles.pdfImageWrapper} ${selectedTool ? styles.drawing : ''}`}
onMouseMove={(event) => {onMouseMove(event, page)}}
onMouseDown={(event) => {onMouseDown(event, page)}}
onMouseMove={(event) => {
onMouseMove(event, page)
}}
onMouseDown={(event) => {
onMouseDown(event, page)
}}
>
<img draggable="false" style={{ width: '100%' }} src={page.image}/>
<img
draggable="false"
style={{ width: '100%' }}
src={page.image}
/>
{page.drawnFields.map((drawnField, drawnFieldIndex: number) => {
return (
<div
key={drawnFieldIndex}
onMouseDown={onDrawnFieldMouseDown}
onMouseMove={(event) => { onDranwFieldMouseMove(event, drawnField)}}
onMouseMove={(event) => {
onDranwFieldMouseMove(event, drawnField)
}}
className={styles.drawingRectangle}
style={{
left: `${drawnField.left}px`,
@ -389,41 +442,68 @@ export const DrawPDFFields = (props: Props) => {
>
<span
onMouseDown={onResizeHandleMouseDown}
onMouseMove={(event) => {onResizeHandleMouseMove(event, drawnField)}}
onMouseMove={(event) => {
onResizeHandleMouseMove(event, drawnField)
}}
className={styles.resizeHandle}
></span>
<span
onMouseDown={(event) => {onRemoveHandleMouseDown(event, pdfFileIndex, pdfPageIndex, drawnFieldIndex)}}
onMouseDown={(event) => {
onRemoveHandleMouseDown(
event,
pdfFileIndex,
pdfPageIndex,
drawnFieldIndex
)
}}
className={styles.removeHandle}
>
<Close fontSize='small'/>
<Close fontSize="small" />
</span>
<div
<div
onMouseDown={onUserSelectHandleMouseDown}
className={styles.userSelect}
>
<FormControl fullWidth size='small'>
<FormControl fullWidth size="small">
<InputLabel id="counterparts">Counterpart</InputLabel>
<Select
value={drawnField.counterpart}
onChange={(event) => { drawnField.counterpart = event.target.value; refreshPdfFiles() }}
onChange={(event) => {
drawnField.counterpart = event.target.value
refreshPdfFiles()
}}
labelId="counterparts"
label="Counterparts"
>
{props.users.map((user, index) => {
let displayValue = truncate(hexToNpub(user.pubkey), {
length: 16
})
let displayValue = truncate(
hexToNpub(user.pubkey),
{
length: 16
}
)
const metadata = props.metadata[user.pubkey]
if (metadata) {
displayValue = truncate(metadata.name || metadata.display_name || metadata.username, {
length: 16
})
displayValue = truncate(
metadata.name ||
metadata.display_name ||
metadata.username,
{
length: 16
}
)
}
return <MenuItem key={index} value={hexToNpub(user.pubkey)}>{displayValue}</MenuItem>
return (
<MenuItem
key={index}
value={hexToNpub(user.pubkey)}
>
{displayValue}
</MenuItem>
)
})}
</Select>
</FormControl>
@ -435,13 +515,13 @@ export const DrawPDFFields = (props: Props) => {
)
})}
</Box>
)
)
}
if (parsingPdf) {
return (
<Box sx={{ width: '100%', textAlign: 'center' }}>
<CircularProgress/>
<CircularProgress />
</Box>
)
}
@ -454,22 +534,28 @@ export const DrawPDFFields = (props: Props) => {
<Box>
<Box sx={{ mt: 1 }}>
<Typography sx={{ mb: 1 }}>Draw fields on the PDFs:</Typography>
{pdfFiles.map((pdfFile, pdfFileIndex: number) => {
return (
<Accordion key={pdfFileIndex} expanded={pdfFile.expanded} onChange={(_event, expanded) => {handleAccordionExpandChange(expanded, pdfFile)}}>
<Accordion
key={pdfFileIndex}
expanded={pdfFile.expanded}
onChange={(_event, expanded) => {
handleAccordionExpandChange(expanded, pdfFile)
}}
>
<AccordionSummary
expandIcon={<ExpandMore />}
aria-controls={`panel${pdfFileIndex}-content`}
id={`panel${pdfFileIndex}header`}
>
<PictureAsPdf sx={{ mr: 1 }}/>
<PictureAsPdf sx={{ mr: 1 }} />
{pdfFile.file.name}
</AccordionSummary>
<AccordionDetails>
{getPdfPages(pdfFile, pdfFileIndex)}
</AccordionDetails>
</Accordion>
</Accordion>
)
})}
</Box>
@ -477,16 +563,22 @@ export const DrawPDFFields = (props: Props) => {
{showDrawToolBox && (
<Box className={styles.drawToolBoxContainer}>
<Box className={styles.drawToolBox}>
{toolbox.filter(drawTool => drawTool.active).map((drawTool: DrawTool, index: number) => {
return (
<Box
key={index}
onClick={() => {handleToolSelect(drawTool)}} className={`${styles.toolItem} ${selectedTool?.identifier === drawTool.identifier ? styles.selected : ''}`}>
{ drawTool.icon }
{ drawTool.label }
</Box>
)
})}
{toolbox
.filter((drawTool) => drawTool.active)
.map((drawTool: DrawTool, index: number) => {
return (
<Box
key={index}
onClick={() => {
handleToolSelect(drawTool)
}}
className={`${styles.toolItem} ${selectedTool?.identifier === drawTool.identifier ? styles.selected : ''}`}
>
{drawTool.icon}
{drawTool.label}
</Box>
)
})}
</Box>
</Box>
)}

View File

@ -1,6 +1,6 @@
import { PdfFile } from '../../types/drawing.ts'
import { CurrentUserMark } from '../../types/mark.ts'
import PdfPageItem from './PdfPageItem.tsx';
import PdfPageItem from './PdfPageItem.tsx'
interface PdfItemProps {
pdfFile: PdfFile
@ -13,23 +13,31 @@ interface PdfItemProps {
/**
* Responsible for displaying pages of a single Pdf File.
*/
const PdfItem = ({ pdfFile, currentUserMarks, handleMarkClick, selectedMarkValue, selectedMark }: PdfItemProps) => {
const filterByPage = (marks: CurrentUserMark[], page: number): CurrentUserMark[] => {
return marks.filter((m) => m.mark.location.page === page);
const PdfItem = ({
pdfFile,
currentUserMarks,
handleMarkClick,
selectedMarkValue,
selectedMark
}: PdfItemProps) => {
const filterByPage = (
marks: CurrentUserMark[],
page: number
): CurrentUserMark[] => {
return marks.filter((m) => m.mark.location.page === page)
}
return (
pdfFile.pages.map((page, i) => {
return (
<PdfPageItem
page={page}
key={i}
currentUserMarks={filterByPage(currentUserMarks, i)}
handleMarkClick={handleMarkClick}
selectedMarkValue={selectedMarkValue}
selectedMark={selectedMark}
/>
)
}))
return pdfFile.pages.map((page, i) => {
return (
<PdfPageItem
page={page}
key={i}
currentUserMarks={filterByPage(currentUserMarks, i)}
handleMarkClick={handleMarkClick}
selectedMarkValue={selectedMarkValue}
selectedMark={selectedMark}
/>
)
})
}
export default PdfItem
export default PdfItem

View File

@ -12,14 +12,18 @@ interface PdfMarkItemProps {
/**
* Responsible for display an individual Pdf Mark.
*/
const PdfMarkItem = ({ selectedMark, handleMarkClick, selectedMarkValue, userMark }: PdfMarkItemProps) => {
const { location } = userMark.mark;
const handleClick = () => handleMarkClick(userMark.mark.id);
const getMarkValue = () => (
const PdfMarkItem = ({
selectedMark,
handleMarkClick,
selectedMarkValue,
userMark
}: PdfMarkItemProps) => {
const { location } = userMark.mark
const handleClick = () => handleMarkClick(userMark.mark.id)
const getMarkValue = () =>
selectedMark?.mark.id === userMark.mark.id
? selectedMarkValue
: userMark.mark.value
)
return (
<div
onClick={handleClick}
@ -30,8 +34,10 @@ const PdfMarkItem = ({ selectedMark, handleMarkClick, selectedMarkValue, userMar
width: inPx(location.width),
height: inPx(location.height)
}}
>{getMarkValue()}</div>
>
{getMarkValue()}
</div>
)
}
export default PdfMarkItem
export default PdfMarkItem

View File

@ -6,17 +6,17 @@ import React, { useState, useEffect } from 'react'
import {
findNextCurrentUserMark,
isCurrentUserMarksComplete,
updateCurrentUserMarks,
updateCurrentUserMarks
} from '../../utils'
import { EMPTY } from '../../utils/const.ts'
import { Container } from '../Container'
import styles from '../../pages/sign/style.module.scss'
interface PdfMarkingProps {
files: { pdfFile: PdfFile, filename: string, hash: string | null }[],
currentUserMarks: CurrentUserMark[],
setIsReadyToSign: (isReadyToSign: boolean) => void,
setCurrentUserMarks: (currentUserMarks: CurrentUserMark[]) => void,
files: { pdfFile: PdfFile; filename: string; hash: string | null }[]
currentUserMarks: CurrentUserMark[]
setIsReadyToSign: (isReadyToSign: boolean) => void
setCurrentUserMarks: (currentUserMarks: CurrentUserMark[]) => void
setUpdatedMarks: (markToUpdate: Mark) => void
}
@ -35,21 +35,21 @@ const PdfMarking = (props: PdfMarkingProps) => {
setUpdatedMarks
} = props
const [selectedMark, setSelectedMark] = useState<CurrentUserMark | null>(null)
const [selectedMarkValue, setSelectedMarkValue] = useState<string>("")
const [selectedMarkValue, setSelectedMarkValue] = useState<string>('')
useEffect(() => {
setSelectedMark(findNextCurrentUserMark(currentUserMarks) || null)
}, [currentUserMarks])
const handleMarkClick = (id: number) => {
const nextMark = currentUserMarks.find((mark) => mark.mark.id === id);
setSelectedMark(nextMark!);
setSelectedMarkValue(nextMark?.mark.value ?? EMPTY);
const nextMark = currentUserMarks.find((mark) => mark.mark.id === id)
setSelectedMark(nextMark!)
setSelectedMarkValue(nextMark?.mark.value ?? EMPTY)
}
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (!selectedMarkValue || !selectedMark) return;
event.preventDefault()
if (!selectedMarkValue || !selectedMark) return
const updatedMark: CurrentUserMark = {
...selectedMark,
@ -61,7 +61,10 @@ const PdfMarking = (props: PdfMarkingProps) => {
}
setSelectedMarkValue(EMPTY)
const updatedCurrentUserMarks = updateCurrentUserMarks(currentUserMarks, updatedMark);
const updatedCurrentUserMarks = updateCurrentUserMarks(
currentUserMarks,
updatedMark
)
setCurrentUserMarks(updatedCurrentUserMarks)
setSelectedMark(findNextCurrentUserMark(updatedCurrentUserMarks) || null)
console.log(isCurrentUserMarksComplete(updatedCurrentUserMarks))
@ -69,32 +72,32 @@ const PdfMarking = (props: PdfMarkingProps) => {
setUpdatedMarks(updatedMark.mark)
}
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setSelectedMarkValue(event.target.value)
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) =>
setSelectedMarkValue(event.target.value)
return (
<>
<Container className={styles.container}>
{
currentUserMarks?.length > 0 && (
<PdfView
files={files}
handleMarkClick={handleMarkClick}
selectedMarkValue={selectedMarkValue}
selectedMark={selectedMark}
currentUserMarks={currentUserMarks}
/>)}
{
selectedMark !== null && (
<MarkFormField
handleSubmit={handleSubmit}
handleChange={handleChange}
selectedMark={selectedMark}
selectedMarkValue={selectedMarkValue}
/>
)}
{currentUserMarks?.length > 0 && (
<PdfView
files={files}
handleMarkClick={handleMarkClick}
selectedMarkValue={selectedMarkValue}
selectedMark={selectedMark}
currentUserMarks={currentUserMarks}
/>
)}
{selectedMark !== null && (
<MarkFormField
handleSubmit={handleSubmit}
handleChange={handleChange}
selectedMark={selectedMark}
selectedMarkValue={selectedMarkValue}
/>
)}
</Container>
</>
)
}
export default PdfMarking
export default PdfMarking

View File

@ -13,7 +13,13 @@ interface PdfPageProps {
/**
* Responsible for rendering a single Pdf Page and its Marks
*/
const PdfPageItem = ({ page, currentUserMarks, handleMarkClick, selectedMarkValue, selectedMark }: PdfPageProps) => {
const PdfPageItem = ({
page,
currentUserMarks,
handleMarkClick,
selectedMarkValue,
selectedMark
}: PdfPageProps) => {
return (
<div
className={styles.pdfImageWrapper}
@ -23,24 +29,18 @@ const PdfPageItem = ({ page, currentUserMarks, handleMarkClick, selectedMarkValu
marginTop: '10px'
}}
>
<img
draggable="false"
src={page.image}
style={{ width: '100%'}}
/>
{
currentUserMarks.map((m, i) => (
<PdfMarkItem
key={i}
handleMarkClick={handleMarkClick}
selectedMarkValue={selectedMarkValue}
userMark={m}
selectedMark={selectedMark}
<img draggable="false" src={page.image} style={{ width: '100%' }} />
{currentUserMarks.map((m, i) => (
<PdfMarkItem
key={i}
handleMarkClick={handleMarkClick}
selectedMarkValue={selectedMarkValue}
userMark={m}
selectedMark={selectedMark}
/>
))}
</div>
)
}
export default PdfPageItem
export default PdfPageItem

View File

@ -4,7 +4,7 @@ import PdfItem from './PdfItem.tsx'
import { CurrentUserMark } from '../../types/mark.ts'
interface PdfViewProps {
files: { pdfFile: PdfFile, filename: string, hash: string | null }[]
files: { pdfFile: PdfFile; filename: string; hash: string | null }[]
currentUserMarks: CurrentUserMark[]
handleMarkClick: (id: number) => 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 (
<Box sx={{ width: '100%' }}>
{
files.map(({ pdfFile, hash }, i) => {
if (!hash) return
return (
<PdfItem
pdfFile={pdfFile}
key={i}
currentUserMarks={filterByFile(currentUserMarks, hash)}
selectedMark={selectedMark}
handleMarkClick={handleMarkClick}
selectedMarkValue={selectedMarkValue}
{files.map(({ pdfFile, hash }, i) => {
if (!hash) return
return (
<PdfItem
pdfFile={pdfFile}
key={i}
currentUserMarks={filterByFile(currentUserMarks, hash)}
selectedMark={selectedMark}
handleMarkClick={handleMarkClick}
selectedMarkValue={selectedMarkValue}
/>
)
})
}
)
})}
</Box>
)
}
export default PdfView;
export default PdfView

View File

@ -111,7 +111,9 @@ button:disabled {
/* Fonts */
@font-face {
font-family: 'Roboto';
src: local('Roboto Medium'), local('Roboto-Medium'),
src:
local('Roboto Medium'),
local('Roboto-Medium'),
url('assets/fonts/roboto-medium.woff2') format('woff2'),
url('assets/fonts/roboto-medium.woff') format('woff');
font-weight: 500;
@ -121,7 +123,9 @@ button:disabled {
@font-face {
font-family: 'Roboto';
src: local('Roboto Light'), local('Roboto-Light'),
src:
local('Roboto Light'),
local('Roboto-Light'),
url('assets/fonts/roboto-light.woff2') format('woff2'),
url('assets/fonts/roboto-light.woff') format('woff');
font-weight: 300;
@ -131,7 +135,9 @@ button:disabled {
@font-face {
font-family: 'Roboto';
src: local('Roboto Bold'), local('Roboto-Bold'),
src:
local('Roboto Bold'),
local('Roboto-Bold'),
url('assets/fonts/roboto-bold.woff2') format('woff2'),
url('assets/fonts/roboto-bold.woff') format('woff');
font-weight: bold;
@ -141,10 +147,12 @@ button:disabled {
@font-face {
font-family: 'Roboto';
src: local('Roboto'), local('Roboto-Regular'),
src:
local('Roboto'),
local('Roboto-Regular'),
url('assets/fonts/roboto-regular.woff2') format('woff2'),
url('assets/fonts/roboto-regular.woff') format('woff');
font-weight: normal;
font-style: normal;
font-display: swap;
}
}

View File

@ -341,29 +341,30 @@ export const CreatePage = () => {
return fileHashes
}
const createMarks = (fileHashes: { [key: string]: string }) : Mark[] => {
return drawnPdfs.flatMap((drawnPdf) => {
const fileHash = fileHashes[drawnPdf.file.name];
return drawnPdf.pages.flatMap((page, index) => {
return page.drawnFields.map((drawnField) => {
return {
type: drawnField.type,
location: {
page: index,
top: drawnField.top,
left: drawnField.left,
height: drawnField.height,
width: drawnField.width,
},
npub: drawnField.counterpart,
pdfFileHash: fileHash
}
const createMarks = (fileHashes: { [key: string]: string }): Mark[] => {
return drawnPdfs
.flatMap((drawnPdf) => {
const fileHash = fileHashes[drawnPdf.file.name]
return drawnPdf.pages.flatMap((page, index) => {
return page.drawnFields.map((drawnField) => {
return {
type: drawnField.type,
location: {
page: index,
top: drawnField.top,
left: drawnField.left,
height: drawnField.height,
width: drawnField.width
},
npub: drawnField.counterpart,
pdfFileHash: fileHash
}
})
})
})
})
.map((mark, index) => {
return {...mark, id: index }
});
return { ...mark, id: index }
})
}
// Handle errors during zip file generation
@ -431,13 +432,9 @@ export const CreatePage = () => {
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 file upload
@ -545,9 +542,7 @@ export const CreatePage = () => {
: viewers.map((viewer) => viewer.pubkey)
).filter((receiver) => receiver !== usersPubkey)
return receivers.map((receiver) =>
sendNotification(receiver, meta)
)
return receivers.map((receiver) => sendNotification(receiver, meta))
}
const handleCreate = async () => {

View File

@ -15,8 +15,8 @@ interface MarkFormFieldProps {
* Responsible for rendering a form field connected to a mark and keeping track of its value.
*/
const MarkFormField = (props: MarkFormFieldProps) => {
const { handleSubmit, handleChange, selectedMark, selectedMarkValue } = props;
const getSubmitButton = () => selectedMark.isLast ? 'Complete' : 'Next';
const { handleSubmit, handleChange, selectedMark, selectedMarkValue } = props
const getSubmitButton = () => (selectedMark.isLast ? 'Complete' : 'Next')
return (
<div className={styles.fixedBottomForm}>
<Box component="form" onSubmit={handleSubmit}>
@ -34,4 +34,4 @@ const MarkFormField = (props: MarkFormFieldProps) => {
)
}
export default MarkFormField;
export default MarkFormField

View File

@ -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,
getFilesWithHashes,
getHash,
hexToNpub,
isOnline, loadZip,
isOnline,
loadZip,
now,
npubToHex,
parseJson,
@ -41,7 +44,8 @@ import { getLastSignersSig } from '../../utils/sign.ts'
import {
filterMarksByPubkey,
getCurrentUserMarks,
isCurrentUserMarksComplete, updateMarks
isCurrentUserMarksComplete,
updateMarks
} from '../../utils'
import PdfMarking from '../../components/PDFView/PdfMarking.tsx'
enum SignedStatus {
@ -81,7 +85,7 @@ export const SignPage = () => {
const [signers, setSigners] = useState<`npub1${string}`[]>([])
const [viewers, setViewers] = useState<`npub1${string}`[]>([])
const [marks, setMarks] = useState<Mark[] >([])
const [marks, setMarks] = useState<Mark[]>([])
const [creatorFileHashes, setCreatorFileHashes] = useState<{
[key: string]: string
}>({})
@ -100,8 +104,10 @@ export const SignPage = () => {
const [authUrl, setAuthUrl] = useState<string>()
const nostrController = NostrController.getInstance()
const [currentUserMarks, setCurrentUserMarks] = useState<CurrentUserMark[]>([]);
const [isReadyToSign, setIsReadyToSign] = useState(false);
const [currentUserMarks, setCurrentUserMarks] = useState<CurrentUserMark[]>(
[]
)
const [isReadyToSign, setIsReadyToSign] = useState(false)
useEffect(() => {
if (signers.length > 0) {
@ -192,13 +198,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)
Review

We should remove the comment or explain why we keep it

We should remove the comment or explain why we keep it
Review

Removed, it's no longer needed.

Removed, it's no longer needed.
setIsReadyToSign(isCurrentUserMarksComplete(currentUserMarks))
}
@ -307,7 +316,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 +357,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)
@ -439,7 +448,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 +529,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 +541,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 +612,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 +702,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 +926,13 @@ export const SignPage = () => {
)
}
return <PdfMarking
files={getFilesWithHashes(files, currentFileHashes)}
currentUserMarks={currentUserMarks}
setIsReadyToSign={setIsReadyToSign}
setCurrentUserMarks={setCurrentUserMarks}
setUpdatedMarks={setUpdatedMarks}
/>
return (
<PdfMarking
files={getFilesWithHashes(files, currentFileHashes)}
currentUserMarks={currentUserMarks}
setIsReadyToSign={setIsReadyToSign}
setCurrentUserMarks={setCurrentUserMarks}
setUpdatedMarks={setUpdatedMarks}
/>
)
}

View File

@ -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,10 @@ export const VerifyPage = () => {
setIsLoading(true)
setLoadingSpinnerDesc('Signing nostr event')
if (!meta) return;
if (!meta) return
const prevSig = getLastSignersSig(meta, signers)
if (!prevSig) return;
if (!prevSig) return
const signedEvent = await signEventForMetaFile(
JSON.stringify({ prevSig }),
@ -406,10 +409,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()

View File

@ -9,7 +9,7 @@ export interface MouseState {
}
export interface PdfFile {
file: File,
file: File
pages: PdfPage[]
expanded?: boolean
}
@ -34,7 +34,7 @@ export interface DrawnField {
export interface DrawTool {
identifier: MarkType
label: string
icon: JSX.Element,
icon: JSX.Element
defaultValue?: string
selected?: boolean
active?: boolean
@ -46,4 +46,4 @@ export enum MarkType {
FULLNAME = 'FULLNAME',
DATE = 'DATE',
DATETIME = 'DATETIME'
}
}

View File

@ -1,4 +1,4 @@
import { MarkType } from "./drawing";
import { MarkType } from './drawing'
export interface CurrentUserMark {
mark: Mark
@ -7,18 +7,18 @@ export interface CurrentUserMark {
}
export interface Mark {
id: number;
npub: string;
pdfFileHash: string;
type: MarkType;
location: MarkLocation;
value?: string;
id: number
npub: string
pdfFileHash: string
type: MarkType
location: MarkLocation
value?: string
}
export interface MarkLocation {
top: number;
left: number;
height: number;
width: number;
page: number;
top: number
left: number
height: number
width: number
page: number
}

View File

@ -1,4 +1,4 @@
export interface OutputByType {
export interface OutputByType {
base64: string
string: string
text: string
@ -11,16 +11,18 @@
}
interface InputByType {
base64: string;
string: string;
text: string;
binarystring: string;
array: number[];
uint8array: Uint8Array;
arraybuffer: ArrayBuffer;
blob: Blob;
stream: NodeJS.ReadableStream;
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<InputByType[keyof InputByType]>;
export type InputFileFormat =
| InputByType[keyof InputByType]
| Promise<InputByType[keyof InputByType]>

View File

@ -3,4 +3,4 @@ import { MarkType } from '../types/drawing.ts'
export const EMPTY: string = ''
export const MARK_TYPE_TRANSLATION: { [key: string]: string } = {
[MarkType.FULLNAME.valueOf()]: 'Full Name'
}
}

View File

@ -9,9 +9,12 @@ import { Event } from 'nostr-tools'
* @param marks - default Marks extracted from Meta
* @param signedMetaMarks - signed user Marks extracted from DocSignatures
*/
const getCurrentUserMarks = (marks: Mark[], signedMetaMarks: Mark[]): CurrentUserMark[] => {
const getCurrentUserMarks = (
marks: Mark[],
signedMetaMarks: Mark[]
): CurrentUserMark[] => {
return marks.map((mark, index, arr) => {
const signedMark = signedMetaMarks.find((m) => m.id === mark.id);
const signedMark = signedMetaMarks.find((m) => m.id === mark.id)
if (signedMark && !!signedMark.value) {
mark.value = signedMark.value
}
@ -27,8 +30,10 @@ const getCurrentUserMarks = (marks: Mark[], signedMetaMarks: Mark[]): CurrentUse
* Returns next incomplete CurrentUserMark if there is one
* @param usersMarks
*/
const findNextCurrentUserMark = (usersMarks: CurrentUserMark[]): CurrentUserMark | undefined => {
return usersMarks.find((mark) => !mark.isCompleted);
const findNextCurrentUserMark = (
usersMarks: CurrentUserMark[]
): CurrentUserMark | undefined => {
return usersMarks.find((mark) => !mark.isCompleted)
}
/**
@ -37,7 +42,7 @@ const findNextCurrentUserMark = (usersMarks: CurrentUserMark[]): CurrentUserMark
* @param pubkey
*/
const filterMarksByPubkey = (marks: Mark[], pubkey: string): Mark[] => {
return marks.filter(mark => mark.npub === hexToNpub(pubkey))
return marks.filter((mark) => mark.npub === hexToNpub(pubkey))
}
/**
@ -57,7 +62,9 @@ const extractMarksFromSignedMeta = (meta: Meta): Mark[] => {
* marked as complete.
* @param currentUserMarks
*/
const isCurrentUserMarksComplete = (currentUserMarks: CurrentUserMark[]): boolean => {
const isCurrentUserMarksComplete = (
currentUserMarks: CurrentUserMark[]
): boolean => {
return currentUserMarks.every((mark) => mark.isCompleted)
}
@ -68,7 +75,7 @@ const isCurrentUserMarksComplete = (currentUserMarks: CurrentUserMark[]): boolea
* @param markToUpdate
*/
const updateMarks = (marks: Mark[], markToUpdate: Mark): Mark[] => {
const indexToUpdate = marks.findIndex(mark => mark.id === markToUpdate.id);
const indexToUpdate = marks.findIndex((mark) => mark.id === markToUpdate.id)
return [
...marks.slice(0, indexToUpdate),
markToUpdate,
@ -76,8 +83,13 @@ const updateMarks = (marks: Mark[], markToUpdate: Mark): Mark[] => {
]
}
const updateCurrentUserMarks = (currentUserMarks: CurrentUserMark[], markToUpdate: CurrentUserMark): CurrentUserMark[] => {
const indexToUpdate = currentUserMarks.findIndex((m) => m.mark.id === markToUpdate.mark.id)
const updateCurrentUserMarks = (
currentUserMarks: CurrentUserMark[],
markToUpdate: CurrentUserMark
): CurrentUserMark[] => {
const indexToUpdate = currentUserMarks.findIndex(
(m) => m.mark.id === markToUpdate.mark.id
)
return [
...currentUserMarks.slice(0, indexToUpdate),
markToUpdate,
@ -85,7 +97,7 @@ const updateCurrentUserMarks = (currentUserMarks: CurrentUserMark[], markToUpdat
]
}
const isLast = <T>(index: number, arr: T[]) => (index === (arr.length -1))
const isLast = <T>(index: number, arr: T[]) => index === arr.length - 1
export {
getCurrentUserMarks,
@ -94,5 +106,5 @@ export {
isCurrentUserMarksComplete,
findNextCurrentUserMark,
updateMarks,
updateCurrentUserMarks,
updateCurrentUserMarks
}

View File

@ -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<PdfFile> => {
* @return PdfFile[] - an array of Sigit's internal Pdf File type
*/
const toPdfFiles = async (selectedFiles: File[]): Promise<PdfFile[]> => {
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<string> => {
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<string> => {
* @param data pdf file bytes
*/
const pdfToImages = async (data: any): Promise<PdfPage[]> => {
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<PdfPage[]> => {
* 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<Blob> => {
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<Blob> => {
const pdfBytes = await pdfDoc.save()
return new Blob([pdfBytes], { type: 'application/pdf' })
}
/**
@ -211,9 +214,12 @@ const convertToPdfBlob = async (markedPdfPages: string[]): Promise<Blob> => {
* @param arrayBuffer
* @param fileName
*/
const convertToPdfFile = async (arrayBuffer: ArrayBuffer, fileName: string): Promise<PdfFile> => {
const file = toFile(arrayBuffer, fileName);
return toPdfFile(file);
const convertToPdfFile = async (
arrayBuffer: ArrayBuffer,
fileName: string
): Promise<PdfFile> => {
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,
}
groupMarksByPage
}

View File

@ -5,7 +5,10 @@ import { Meta } from '../types'
* This function returns the signature of last signer
* It will be used in the content of export signature's signedEvent
*/
const getLastSignersSig = (meta: Meta, signers: `npub1${string}`[]): string | null => {
const getLastSignersSig = (
meta: Meta,
signers: `npub1${string}`[]
): string | null => {
// if there're no signers then use creator's signature
if (signers.length === 0) {
try {
@ -21,13 +24,11 @@ const getLastSignersSig = (meta: Meta, signers: `npub1${string}`[]): string | nu
// get the signature of last signer
try {
const lastSignatureEvent: Event = JSON.parse(
meta.docSignatures[lastSigner]
)
const lastSignatureEvent: Event = JSON.parse(meta.docSignatures[lastSigner])
return lastSignatureEvent.sig
} catch (error) {
return null
}
}
export { getLastSignersSig }
export { getLastSignersSig }

View File

@ -73,9 +73,9 @@ export const timeout = (ms: number = 60000) => {
* @param fileHashes
*/
export const getFilesWithHashes = (
files: { [filename: string ]: PdfFile },
files: { [filename: string]: PdfFile },
fileHashes: { [key: string]: string | null }
) => {
) => {
return Object.entries(files).map(([filename, pdfFile]) => {
return { pdfFile, filename, hash: fileHashes[filename] }
})

View File

@ -36,17 +36,12 @@ const readContentOfZipEntry = async <T extends OutputType>(
const loadZip = async (data: InputFileFormat): Promise<JSZip | null> => {
try {
return await JSZip.loadAsync(data);
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;
return null
}
}
export {
readContentOfZipEntry,
loadZip
}
export { readContentOfZipEntry, loadZip }