Merge branch 'issue-99-signing-page-refactor' into issue-99
This commit is contained in:
commit
205b28f820
@ -139,7 +139,7 @@ export const DrawPDFFields = (props: Props) => {
|
||||
* Drawing is finished, resets all the variables used to draw
|
||||
* @param event Mouse event
|
||||
*/
|
||||
const onMouseUp = (event: MouseEvent) => {
|
||||
const onMouseUp = () => {
|
||||
setMouseState((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
@ -187,7 +187,7 @@ export const DrawPDFFields = (props: Props) => {
|
||||
* @param event Mouse event
|
||||
* @param drawnField Which we are moving
|
||||
*/
|
||||
const onDrawnFieldMouseDown = (event: any, drawnField: DrawnField) => {
|
||||
const onDrawnFieldMouseDown = (event: any) => {
|
||||
event.stopPropagation()
|
||||
|
||||
// Proceed only if left click
|
||||
@ -240,7 +240,7 @@ export const DrawPDFFields = (props: Props) => {
|
||||
* @param event Mouse event
|
||||
* @param drawnField which we are resizing
|
||||
*/
|
||||
const onResizeHandleMouseDown = (event: any, drawnField: DrawnField) => {
|
||||
const onResizeHandleMouseDown = (event: any) => {
|
||||
// Proceed only if left click
|
||||
if (event.button !== 0) return
|
||||
|
||||
@ -377,7 +377,7 @@ export const DrawPDFFields = (props: Props) => {
|
||||
return (
|
||||
<div
|
||||
key={drawnFieldIndex}
|
||||
onMouseDown={(event) => { onDrawnFieldMouseDown(event, drawnField) }}
|
||||
onMouseDown={onDrawnFieldMouseDown}
|
||||
onMouseMove={(event) => { onDranwFieldMouseMove(event, drawnField)}}
|
||||
className={styles.drawingRectangle}
|
||||
style={{
|
||||
@ -389,7 +389,7 @@ export const DrawPDFFields = (props: Props) => {
|
||||
}}
|
||||
>
|
||||
<span
|
||||
onMouseDown={(event) => {onResizeHandleMouseDown(event, drawnField)}}
|
||||
onMouseDown={onResizeHandleMouseDown}
|
||||
onMouseMove={(event) => {onResizeHandleMouseMove(event, drawnField)}}
|
||||
className={styles.resizeHandle}
|
||||
></span>
|
||||
@ -400,7 +400,7 @@ export const DrawPDFFields = (props: Props) => {
|
||||
<Close fontSize='small'/>
|
||||
</span>
|
||||
<div
|
||||
onMouseDown={(event) => {onUserSelectHandleMouseDown(event)}}
|
||||
onMouseDown={onUserSelectHandleMouseDown}
|
||||
className={styles.userSelect}
|
||||
>
|
||||
<FormControl fullWidth size='small'>
|
||||
|
@ -10,6 +10,9 @@ interface PdfItemProps {
|
||||
selectedMark: CurrentUserMark | null
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
@ -9,10 +9,9 @@ interface PdfMarkItemProps {
|
||||
selectedMark: CurrentUserMark | null
|
||||
}
|
||||
|
||||
//selectedMark represents the mark that the user is actively editing
|
||||
// selectedMarkValue representsnthe edited value
|
||||
// userMark is part of the overall currentUserMark array
|
||||
|
||||
/**
|
||||
* Responsible for display an individual Pdf Mark.
|
||||
*/
|
||||
const PdfMarkItem = ({ selectedMark, handleMarkClick, selectedMarkValue, userMark }: PdfMarkItemProps) => {
|
||||
const { location } = userMark.mark;
|
||||
const handleClick = () => handleMarkClick(userMark.mark.id);
|
||||
|
@ -4,7 +4,7 @@ import PdfView from './index.tsx'
|
||||
import MarkFormField from '../../pages/sign/MarkFormField.tsx'
|
||||
import { PdfFile } from '../../types/drawing.ts'
|
||||
import { CurrentUserMark, Mark } from '../../types/mark.ts'
|
||||
import { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import {
|
||||
findNextCurrentUserMark,
|
||||
isCurrentUserMarksComplete,
|
||||
@ -21,6 +21,12 @@ interface PdfMarkingProps {
|
||||
setUpdatedMarks: (markToUpdate: Mark) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Top-level component responsible for displaying Pdfs, Pages, and Marks,
|
||||
* as well as tracking if the document is ready to be signed.
|
||||
* @param props
|
||||
* @constructor
|
||||
*/
|
||||
const PdfMarking = (props: PdfMarkingProps) => {
|
||||
const {
|
||||
files,
|
||||
@ -42,7 +48,7 @@ const PdfMarking = (props: PdfMarkingProps) => {
|
||||
setSelectedMarkValue(nextMark?.mark.value ?? EMPTY);
|
||||
}
|
||||
|
||||
const handleSubmit = (event: any) => {
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
if (!selectedMarkValue || !selectedMark) return;
|
||||
|
||||
@ -64,7 +70,7 @@ const PdfMarking = (props: PdfMarkingProps) => {
|
||||
setUpdatedMarks(updatedMark.mark)
|
||||
}
|
||||
|
||||
const handleChange = (event: any) => setSelectedMarkValue(event.target.value)
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setSelectedMarkValue(event.target.value)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -10,6 +10,9 @@ interface PdfPageProps {
|
||||
selectedMark: CurrentUserMark | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for rendering a single Pdf Page and its Marks
|
||||
*/
|
||||
const PdfPageItem = ({ page, currentUserMarks, handleMarkClick, selectedMarkValue, selectedMark }: PdfPageProps) => {
|
||||
return (
|
||||
<div
|
||||
|
@ -11,6 +11,9 @@ interface PdfViewProps {
|
||||
selectedMark: CurrentUserMark | null
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
@ -63,7 +63,6 @@ import styles from './style.module.scss'
|
||||
import { PdfFile } from '../../types/drawing'
|
||||
import { DrawPDFFields } from '../../components/DrawPDFFields'
|
||||
import { Mark } from '../../types/mark.ts'
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export const CreatePage = () => {
|
||||
const navigate = useNavigate()
|
||||
@ -341,7 +340,7 @@ export const CreatePage = () => {
|
||||
return fileHashes
|
||||
}
|
||||
|
||||
const createMarkConfig = (fileHashes: { [key: string]: string }) : Mark[] => {
|
||||
const createMarks = (fileHashes: { [key: string]: string }) : Mark[] => {
|
||||
return drawnPdfs.flatMap((drawnPdf) => {
|
||||
const fileHash = fileHashes[drawnPdf.file.name];
|
||||
return drawnPdf.pages.flatMap((page, index) => {
|
||||
@ -378,15 +377,13 @@ export const CreatePage = () => {
|
||||
const generateZipFile = async (zip: JSZip): Promise<ArrayBuffer | null> => {
|
||||
setLoadingSpinnerDesc('Generating zip file')
|
||||
|
||||
const arraybuffer = await zip
|
||||
return await zip
|
||||
.generateAsync({
|
||||
type: 'arraybuffer',
|
||||
compression: 'DEFLATE',
|
||||
compressionOptions: { level: 6 }
|
||||
})
|
||||
.catch(handleZipError)
|
||||
|
||||
return arraybuffer
|
||||
}
|
||||
|
||||
// Encrypt the zip file with the generated encryption key
|
||||
@ -433,15 +430,13 @@ export const CreatePage = () => {
|
||||
|
||||
if (!arraybuffer) return null
|
||||
|
||||
const finalZipFile = new File(
|
||||
return new File(
|
||||
[new Blob([arraybuffer])],
|
||||
`${unixNow}.sigit.zip`,
|
||||
{
|
||||
type: 'application/zip'
|
||||
}
|
||||
)
|
||||
|
||||
return finalZipFile
|
||||
}
|
||||
|
||||
// Handle errors during file upload
|
||||
@ -463,14 +458,12 @@ export const CreatePage = () => {
|
||||
type: 'application/sigit'
|
||||
})
|
||||
|
||||
const fileUrl = await uploadToFileStorage(file)
|
||||
return await uploadToFileStorage(file)
|
||||
.then((url) => {
|
||||
toast.success('files.zip uploaded to file storage')
|
||||
return url
|
||||
})
|
||||
.catch(handleUploadError)
|
||||
|
||||
return fileUrl
|
||||
}
|
||||
|
||||
// Manage offline scenarios for signing or viewing the file
|
||||
@ -494,15 +487,13 @@ export const CreatePage = () => {
|
||||
zip.file(file.name, file)
|
||||
})
|
||||
|
||||
const arraybuffer = await zip
|
||||
return await zip
|
||||
.generateAsync({
|
||||
type: 'arraybuffer',
|
||||
compression: 'DEFLATE',
|
||||
compressionOptions: { level: 6 }
|
||||
})
|
||||
.catch(handleZipError)
|
||||
|
||||
return arraybuffer
|
||||
}
|
||||
|
||||
const generateCreateSignature = async (
|
||||
@ -513,9 +504,7 @@ export const CreatePage = () => {
|
||||
) => {
|
||||
const signers = users.filter((user) => user.role === UserRole.signer)
|
||||
const viewers = users.filter((user) => user.role === UserRole.viewer)
|
||||
const markConfig = createMarkConfig(fileHashes)
|
||||
|
||||
console.log('mark config: ', markConfig)
|
||||
const markConfig = createMarks(fileHashes)
|
||||
|
||||
const content: CreateSignatureEventContent = {
|
||||
signers: signers.map((signer) => hexToNpub(signer.pubkey)),
|
||||
@ -555,11 +544,9 @@ export const CreatePage = () => {
|
||||
: viewers.map((viewer) => viewer.pubkey)
|
||||
).filter((receiver) => receiver !== usersPubkey)
|
||||
|
||||
const promises = receivers.map((receiver) =>
|
||||
return receivers.map((receiver) =>
|
||||
sendNotification(receiver, meta)
|
||||
)
|
||||
|
||||
return promises
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { CurrentUserMark, Mark } from '../../types/mark.ts'
|
||||
import { CurrentUserMark } from '../../types/mark.ts'
|
||||
import styles from './style.module.scss'
|
||||
import { Box, Button, TextField } from '@mui/material'
|
||||
|
||||
@ -11,6 +11,9 @@ interface MarkFormFieldProps {
|
||||
selectedMarkValue: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 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';
|
||||
|
@ -337,15 +337,13 @@ export const SignPage = () => {
|
||||
|
||||
if (!keysFileContent) return null
|
||||
|
||||
const parsedJSON = await parseJson<{ sender: string; keys: string[] }>(
|
||||
return await parseJson<{ sender: string; keys: string[] }>(
|
||||
keysFileContent
|
||||
).catch((err) => {
|
||||
console.log(`Error parsing content of keys.json:`, err)
|
||||
toast.error(err.message || `Error parsing content of keys.json`)
|
||||
return null
|
||||
})
|
||||
|
||||
return parsedJSON
|
||||
}
|
||||
|
||||
const decrypt = async (file: File) => {
|
||||
@ -603,15 +601,13 @@ export const SignPage = () => {
|
||||
|
||||
if (!arraybuffer) return null
|
||||
|
||||
const finalZipFile = new File(
|
||||
return new File(
|
||||
[new Blob([arraybuffer])],
|
||||
`${unixNow}.sigit.zip`,
|
||||
{
|
||||
type: 'application/zip'
|
||||
}
|
||||
)
|
||||
|
||||
return finalZipFile
|
||||
}
|
||||
|
||||
// Handle errors during zip file generation
|
||||
|
@ -15,40 +15,6 @@ export interface Mark {
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface MarkConfig {
|
||||
/**
|
||||
* @key user npub
|
||||
*/
|
||||
[key: string]: User
|
||||
}
|
||||
|
||||
export interface User {
|
||||
/**
|
||||
* @key png (pdf page) file hash
|
||||
*/
|
||||
[key: string]: MarkConfigDetails[]
|
||||
}
|
||||
|
||||
export interface MarkDetails {
|
||||
/**
|
||||
* @key coords in format X:10;Y:50
|
||||
*/
|
||||
[key: string]: MarkValue
|
||||
}
|
||||
|
||||
export interface MarkValue {
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface MarkConfigDetails {
|
||||
type: MarkType;
|
||||
/**
|
||||
* Coordinates in format: X:10;Y:50
|
||||
*/
|
||||
location: MarkLocation;
|
||||
value?: MarkValue
|
||||
}
|
||||
|
||||
export interface MarkLocation {
|
||||
top: number;
|
||||
left: number;
|
||||
|
@ -6,4 +6,4 @@ export * from './nostr'
|
||||
export * from './string'
|
||||
export * from './zip'
|
||||
export * from './utils'
|
||||
export { extractMarksFromSignedMeta } from './mark.ts'
|
||||
export * from './mark'
|
||||
|
@ -85,7 +85,7 @@ const updateCurrentUserMarks = (currentUserMarks: CurrentUserMark[], markToUpdat
|
||||
]
|
||||
}
|
||||
|
||||
const isLast = (index: number, arr: any[]) => (index === (arr.length -1))
|
||||
const isLast = <T>(index: number, arr: T[]) => (index === (arr.length -1))
|
||||
|
||||
export {
|
||||
getCurrentUserMarks,
|
||||
|
@ -12,11 +12,10 @@ import { toast } from 'react-toastify'
|
||||
import { NostrController } from '../controllers'
|
||||
import { AuthState } from '../store/auth/types'
|
||||
import store from '../store/store'
|
||||
import { CreateSignatureEventContent, Meta, SignedEventContent } from '../types'
|
||||
import { CreateSignatureEventContent, Meta } from '../types'
|
||||
import { hexToNpub, now } from './nostr'
|
||||
import { parseJson } from './string'
|
||||
import { hexToBytes } from '@noble/hashes/utils'
|
||||
import { Mark } from '../types/mark.ts'
|
||||
|
||||
/**
|
||||
* Uploads a file to a file storage service.
|
||||
@ -84,14 +83,12 @@ export const signEventForMetaFile = async (
|
||||
}
|
||||
|
||||
// Sign the event
|
||||
const signedEvent = await nostrController.signEvent(event).catch((err) => {
|
||||
return await nostrController.signEvent(event).catch((err) => {
|
||||
console.error(err)
|
||||
toast.error(err.message || 'Error occurred in signing nostr event')
|
||||
setIsLoading(false) // Set loading state to false
|
||||
return null
|
||||
})
|
||||
|
||||
return signedEvent // Return the signed event
|
||||
}
|
||||
|
||||
/**
|
||||
@ -265,10 +262,3 @@ export const extractZipUrlAndEncryptionKey = async (meta: Meta) => {
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const extractMarksFromSignedMeta = (meta: Meta): Mark[] => {
|
||||
return Object.values(meta.docSignatures)
|
||||
.map((val: string) => JSON.parse(val as string))
|
||||
.map((val: Event) => JSON.parse(val.content))
|
||||
.flatMap((val: SignedEventContent) => val.marks);
|
||||
}
|
||||
|
@ -22,8 +22,9 @@ const readContentOfZipEntry = async <T extends OutputType>(
|
||||
return null
|
||||
}
|
||||
|
||||
// Read the content of the zip entry asynchronously
|
||||
const fileContent = await zipEntry.async(outputType).catch((err) => {
|
||||
// Read and return the content of the zip entry asynchronously
|
||||
// or null if an error has occurred
|
||||
return await zipEntry.async(outputType).catch((err) => {
|
||||
// Handle any errors that occur during the read operation
|
||||
console.log(`Error reading content of ${filePath}:`, err)
|
||||
toast.error(
|
||||
@ -31,9 +32,6 @@ const readContentOfZipEntry = async <T extends OutputType>(
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
// Return the file content or null if an error occurred
|
||||
return fileContent
|
||||
}
|
||||
|
||||
const loadZip = async (data: InputFileFormat): Promise<JSZip | null> => {
|
||||
|
Loading…
Reference in New Issue
Block a user