NotesWhat is notes.io?

Notes brand slogan

Notes - notes.io

/* eslint-disable no-console */
/* eslint-disable no-unused-vars */
import WebViewer from '@pdftron/webviewer';
import { format } from 'date-fns';
import React, { useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { toast } from 'react-toastify';
import Card from '../../components/ui/Card';
import SelectField from '../../components/ui/SelectField';
import { fetchXPDFStrings } from '../../services/meetingService';
import { fileUpload } from '../../share/utils';
import { useWebSocket } from '../../utils/WebSocketContext';
import { parseS3SignedKey } from '../../utils/parseSignedUrl';
import { createTextField, customTools } from '../../utils/share';
const serializer = new XMLSerializer();
const approvalFieldName = 'digitalsign';
const appearance_img_path = '/files/tick.png';
const password = 'Arshad@2937';

function MeetingPdfWebViewer({
selectedDocument,
options,
notaryDetails,
setCertifiedDocs,
documents,
setSelectedDocument,
}) {
const viewer = useRef();
const instance = useRef();
const toolbarNameRef = useRef();
const userRef = useRef({
value: options[0]?.value,
label: options[0]?.text,
});
const [selectedUser, setSelectedUser] = useState({
value: options[0]?.value,
label: options[0]?.text,
});
const { appointmentsId } = useParams();
let user = useSelector(state => state?.auth?.user);
const { connect, getConnectionStatus, status, sendMessage, onMessage } =
useWebSocket();

useEffect(() => {
const establishWebSocketConnection = () => {
const connectionStatus = getConnectionStatus();
if (connectionStatus === 'DISCONNECTED' || connectionStatus === 'ERROR') {
connect(
JSON.stringify({
type: 'joinRoom',
userType: user.userType,
appointmentId: appointmentsId,
participantId: user?.id,
})
);
}
};
onMessage(receivedMessage);
establishWebSocketConnection();
return () => {
onMessage(null);
};
}, [connect, getConnectionStatus, status]);

const receivedMessage = async event => {
if (instance.current) {
const annotationManager = instance.current.Core.annotationManager;
const annotation = JSON.parse(event);

if (annotation.type === 'message') {
if (annotation.xfdfString.includes('widget')) {
const annotations = await annotationManager.importAnnotations(
annotation.xfdfString
);
await annotationManager.drawAnnotationsFromList(annotations);
}
const annotations = await annotationManager.importAnnotationCommand(
annotation.xfdfString
);
await annotationManager.drawAnnotationsFromList(annotations);
} else {
try {
const parsedEvent = JSON.parse(event);
const message = parsedEvent.message;
const messageObject = JSON.parse(message);
if (messageObject.type === 'documentChanged') {
const doc = documents.findIndex(doc =>
doc.document.includes(messageObject.documentId)
);
if (doc >= 0) {
setSelectedDocument(documents[doc]);
}
}
} catch (e) {
// console.log(e);
return;
}
}
}
};
if (!user?.userType) {
user = useSelector(state => state?.guestAuthReducer);
}

const uploadDoc = async blob => {
try {
const fileName = parseS3SignedKey(selectedDocument?.document);
const decodedName = fileName
? fileName.split('_').slice(1).join('_')
: null;
const uploadedFile = await fileUpload(
blob,
`/${appointmentsId}/${decodedName}`
);
const key = uploadedFile?.key;
setCertifiedDocs(prev => ({ ...prev, [decodedName]: key }));
toast.success('document verified');
} catch (error) {
toast.error(
error?.response.data.message ||
error?.message ||
'Something went wrong while uploading the certified image'
);
}
};

useEffect(() => {
if (!instance?.current) {
WebViewer(
{
path: 'webviewer/lib',
initialDoc: selectedDocument?.document,
licenseKey: import.meta.env.VITE_APRYSE_LICENCE_KEY,
documentXFDFRetriever: async () => {
const rows = await fetchXPDFStrings(
parseS3SignedKey(selectedDocument?.document)
);
const annotations = rows?.data[0]?.annotation || [];
return [...annotations.map(a => a?.xfdf)];
},
disabledElements: [
'contextMenuPopup',
'annotationPopup',
'richTextPopup',
'rubberStampToolGroupButton',
'toolsOverlay',
'signatureToolGroupButton',
'toolbarGroup-Edit',
'toggleCompareModeButton',
'coverLayoutButton',
'toggleNotesButton',
'toolbarGroup-Annotate',
'toolbarGroup-Insert',
'toolbarGroup-Shapes',
'viewControlsButton',
'formFieldEditPopup',
'leftPanelButton',
'notesPanel',
],
fullAPI: true,
},
viewer.current
).then(inst => {
instance.current = inst;
inst.UI.setToolbarGroup('toolbarGroup-View');
const { UI, Core } = instance.current;
const {
documentViewer,
Annotations,
annotationManager,
Tools,
PDFNet,
} = Core;

UI.setHeaderItems(header => {
header.push({
type: 'actionButton',
img: '/files/certificate-line.svg',
onClick: async () => {
try {
const document = documentViewer.getDocument();
const xfdfString = await annotationManager.exportAnnotations();
const data = await document.getFileData({
xfdfString,
});
const arr = new Uint8Array(data);

const doc = await PDFNet.PDFDoc.createFromBuffer(arr);

await PDFNet.runWithCleanup(async () => {
await doc.lock();
await doc.createDigitalSignatureField(approvalFieldName);
const foundCertificationField =
await doc.getField(approvalFieldName);
const certificationSigField =
await PDFNet.DigitalSignatureField.createFromField(
foundCertificationField
);
const img = await PDFNet.Image.createFromURL(
doc,
appearance_img_path
);
const certifySignatureWidget =
await PDFNet.SignatureWidget.createWithDigitalSignatureField(
doc,
await PDFNet.Rect.init(0, 100, 200, 150),
certificationSigField
);
await certifySignatureWidget.createSignatureAppearance(img);
const page1 = await doc.getPage(1);
page1.annotPushBack(certifySignatureWidget);
certificationSigField.setDocumentPermissions(
PDFNet.DigitalSignatureField.DocumentPermissions
.e_no_changes_allowed
);
await certificationSigField.signOnNextSaveFromURL(
'/files/2023-07-12 Helleni Moon.pfx',
password
);

const buf = await doc.saveMemoryBuffer(0);
const blob = new Blob([buf], { type: 'application/pdf' });
uploadDoc(blob);
});
} catch (error) {
toast.error('Error while attaching digital certificate');
}
},
title: 'Certify',
});
return header;
});

UI.createToolbarGroup({
name: 'Tools',
dataElementSuffix: 'Draw',
useDefaultElements: false,
children: [
{ type: 'spacer' },
{
type: 'toolGroupButton',
toolGroup: 'freeHandTools',
dataElement: 'freeHandToolGroupButton',
title: 'annotation.freehand',
},
{ type: 'spacer' },
],
});

user.userType === 'notary' &&
customTools.forEach(tool => {
const annotation = Annotations.CustomAnnotation.createFromClass(
tool.name,
Annotations.FreeTextAnnotation
);
annotationManager.registerAnnotationType(
annotation.prototype.elementName,
annotation
);

class CreateTool extends Tools.GenericAnnotationCreateTool {
constructor(documentViewer) {
super(documentViewer, annotation);
}
}

const toolName = `AnnotationCreateRectangleText${tool.name}`;
const toolButtonName = `${tool.name}ToolButton`;

const createTool = new CreateTool(documentViewer);
UI.registerTool(
{
toolName,
toolObject: createTool,
buttonImage: tool.buttonImage,
buttonName: toolButtonName,
buttonGroup: toolButtonName,
toolTip: tool.toolTip,
},
annotation
);
UI.setHeaderItems(header => {
header.getHeader('toolbarGroup-Draw').push({
type: 'toolGroupButton',
toolGroup: toolButtonName,
dataElement: toolButtonName,
title: tool.toolTip,
});
});

createTool.addEventListener('annotationAdded', async annotation => {
createTextField(
instance,
notaryDetails,
annotation.PageNumber,
annotation.X,
annotation.Y,
annotation.Width,
annotation.Height,
tool.value || undefined,
tool.type || undefined,
tool.user || undefined
);
annotationManager.deleteAnnotation(annotation);
});
});

UI.setFitMode('FitWidth');

if (user?.userType === 'notary') {
// Core.annotationManager.promoteUserToAdmin();
// Core.annotationManager.disableReadOnlyMode();

UI.enableFeatures([UI.Feature.Initials]);
UI.setToolbarGroup('toolbarGroup-View');
UI.setHeaderItems(header => {
header.getHeader('toolbarGroup-Forms').delete(7);
header.getHeader('toolbarGroup-FillAndSign').delete(11);
return header;
});
} else {
Core.annotationManager.demoteUserFromAdmin();
// Core.annotationManager.disableReadOnlyMode();
UI.disableElements([
'header',
'rubberStampToolGroupButton',
'freeTextToolGroupButton',
'stampToolGroupButton',
'fileAttachmentToolGroupButton',
'calloutToolGroupButton',
'undoButton',
'redoButton',
'eraserToolButton',
'crossStampToolButton',
'checkStampToolButton',
'dotStampToolButton',
'dateFreeTextToolButton',
]);
UI.enableFeatures([UI.Feature.Initials]);
}
const addSignaturePermissions = () => {
const signatureTool = documentViewer.getTool(
'AnnotationCreateSignature'
);

signatureTool.addEventListener(
'locationSelected',
(coords, annot) => {
UI.disableElements(['signatureModal']);

if (!annot.getCustomData('email')) return;
if (annot.getCustomData('email') === user?.email) {
if (
annot.getAssociatedSignatureAnnotation() ||
annot.isSignedByAppearance()
) {
UI.enableElements('annotationPopup');
} else {
UI.enableElements(['signatureModal']);
}
} else {
return;
}
}
);
};
addSignaturePermissions();
documentViewer.addEventListener('documentLoaded', async () => {
Core.annotationManager.setCurrentUser(user?.email);

if (!instance.Core) return;
const signatureTool =
new instance.Core.Annotations.SignatureWidgetAnnotation({});

signatureTool.setSigningMode(
Tools.SignatureCreateTool.SigningModes.APPEARANCE
);

const originalCreateInnerElement =
Annotations.SignatureWidgetAnnotation.prototype.createInnerElement;

Annotations.SignatureWidgetAnnotation.prototype.createInnerElement =
function () {
const ele = originalCreateInnerElement.apply(this, arguments);

UI.signSignatureWidget(ele);
ele.addEventListener('click', () => {
UI.closeElements('signatureModal');
UI.disableElements(['toolsOverlay']);

if (this.getCustomData('email') === user?.email) {
if (
this.getAssociatedSignatureAnnotation() ||
this.isSignedByAppearance()
) {
UI.enableElements('annotationPopup');
} else {
UI.openElements('signatureModal');
}
} else {
return;
}
});

return ele;
};
});

Core.annotationManager.addEventListener(
'annotationSelected',
async (annotations, action) => {
Core.annotationManager.setPermissionCheckCallback(
(author, annotation) => {
const defaultPermission =
annotation.Author === Core.annotationManager.getCurrentUser();
const customPermission = annotation.Opacity === 0.5;
const emailPermission =
annotation?.getCustomData('email') ===
Core.annotationManager.getCurrentUser();

const notary =
options[0]?.value === Core.annotationManager.getCurrentUser();

return (
defaultPermission ||
customPermission ||
emailPermission ||
notary
);
}
);
}
);
const formFieldCreationManager =
Core.annotationManager.getFormFieldCreationManager();
formFieldCreationManager.addEventListener(
'formFieldCreationModeEnded',
async () => {
sendSignatureWidgetAnnotations();
}
);

formFieldCreationManager.addEventListener(
'formFieldCreationModeStarted',
async () => {
sendSignatureWidgetAnnotations();
}
);

annotationManager.addEventListener(
'annotationChanged',
async (annotations, action, { imported }) => {
const isInFormFieldCreationMode =
formFieldCreationManager.isInFormFieldCreationMode();

if (isInFormFieldCreationMode) {
annotationChange(
annotations,
action,
imported,
Core,
annotationManager,
Annotations
);

return;
}

if (imported) {
return;
}

annotationChange(
annotations,
action,
imported,
Core,
annotationManager,
Annotations
);
checkAndSendAnnotation();
}
);
});
}
}, []);

const sendSignatureWidgetAnnotations = async () => {
try {
const annots =
await instance.current.Core.annotationManager.exportAnnotations();
const parser = new DOMParser();
const commandData = parser.parseFromString(annots, 'text/xml');
const addedAnnots = commandData.getElementsByTagName('widget')[0];
const widgetId = addedAnnots?.getAttribute('name');

if (annots.includes('widget')) {
sendAnnotationChange(annots, 'add-widget', widgetId);
}
} catch (e) {
console.log('error in export', e);
}
};

const checkAndSendAnnotation = async () => {
const xfdfString =
await instance.current.Core.annotationManager.exportAnnotationCommand();
const parser = new DOMParser();
const commandData = parser.parseFromString(xfdfString, 'text/xml');
const addedAnnots = commandData.getElementsByTagName('add')[0];
const modifiedAnnots = commandData.getElementsByTagName('modify')[0];
const deletedAnnots = commandData.getElementsByTagName('delete')[0];

// List of added annotations
addedAnnots.childNodes.forEach(child => {
sendAnnotationChange(child, 'add');
});

// List of modified annotations
modifiedAnnots.childNodes.forEach(child => {
sendAnnotationChange(child, 'modify');
});

// List of deleted annotations
deletedAnnots.childNodes.forEach(child => {
sendAnnotationChange(child, 'delete');
});
};

const sendAnnotationChange = (annotation, action, id) => {
if (
annotation.nodeType !== annotation.TEXT_NODE ||
action == 'add-widget'
) {
const connectionStatus = getConnectionStatus();
if (connectionStatus !== 'CONNECTED') {
connect(
JSON.stringify({
type: 'joinRoom',
userType: user.userType,
appointmentId: appointmentsId,
participantId: user?.id,
})
);
}
const sendData = {
type: 'message',
appointmentId: appointmentsId,
documentId: parseS3SignedKey(selectedDocument?.document),
};

if (action === 'add-widget') {
sendData.annotationId = id;
sendData.xfdfString = annotation;
} else {
const annotationString = serializer.serializeToString(annotation);
if (action === 'delete') {
sendData.annotationId = annotation.textContent;
} else {
sendData.annotationId = annotation.getAttribute('name');
}
sendData.xfdfString = convertToXfdf(annotationString, action);
}
sendMessage(JSON.stringify(sendData));
}
};

const convertToXfdf = (changedAnnotation, action) => {
let xfdfString = `<?xml version="1.0" encoding="UTF-8" ?><xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve"><fields />`;
if (action === 'add') {
xfdfString += `<add>${changedAnnotation}</add><modify /><delete />`;
} else if (action === 'modify') {
xfdfString += `<add /><modify>${changedAnnotation}</modify><delete />`;
} else if (action === 'delete') {
xfdfString += `<add /><modify /><delete>${changedAnnotation}</delete>`;
}
xfdfString += `</xfdf>`;
return xfdfString;
};

const annotationChange = (
annotations,
action,
imported,
Core,
annotationManager,
Annotations
) => {
console.log(annotations, action);

annotations?.forEach(async annotation => {
if (
annotation?.getCustomData('user') === 'notary' &&
annotation?.getCustomData('stamp') === 'notary-stamp'
) {
const width = annotation.Width;
const height = annotation.Height;
const x = annotation.X;
const y = annotation.Y;
const pageNumber = annotation.PageNumber;
annotationManager.deleteAnnotations([annotation]);
const stampAnnot = new Annotations.StampAnnotation();
stampAnnot.PageNumber = pageNumber;
stampAnnot.X = x;
stampAnnot.Y = y;
stampAnnot.Width = width;
stampAnnot.Height = height;
stampAnnot.NoMove = true;
stampAnnot.NoResize = true;
stampAnnot.setCustomData('user', 'notary');
const keepAsSVG = false;
stampAnnot.setImageData('/files/stamp.svg', keepAsSVG);
stampAnnot.Author = annotationManager.getCurrentUser();
annotationManager.addAnnotation(stampAnnot);
annotationManager.redrawAnnotation(stampAnnot);
} else if (
annotation.getCustomData('date') === 'date' &&
annotation?.getCustomData('email') === user?.email
) {
const annotationManager = instance?.current?.Core?.annotationManager;
annotation.setContents(format(new Date(), 'MM/dd/yyy'));
annotationManager.updateAnnotation(annotation);
annotationManager.redrawAnnotation(annotation);
checkAndSendAnnotation();
} else if (
annotation.getCustomData('name') === 'name' &&
annotation?.getCustomData('email') === user?.email
) {
const annotationManager = instance?.current?.Core?.annotationManager;
annotation.setContents(`${user?.firstName} ${user?.lastName}`);
annotationManager.updateAnnotation(annotation);
annotationManager.redrawAnnotation(annotation);
checkAndSendAnnotation();
} else if (user?.userType === 'notary') {
if (action === 'add' && !imported) {
const userEmail = annotation.getCustomData('email');
const annotationList = annotationManager.getAnnotationsList();
console.log(userEmail, 'emailllllllllll', annotationList, annotation);
// const newAnnotations = annotations.filter(annotation => {
// const widgetannot = annotationList.find(
// ann => ann.Subject === 'Widget'
// );
// const widgetEmail = widgetannot.getCustomData('email');
// const existingAnnotation = annotationList.find(
// ann =>
// ann.getCustomData('trn-editing-rectangle-id') ===
// annotation.Id
// );

// console.log(existingAnnotation, 'exist');
// existingAnnotation.setCustomData('email', widgetEmail);
// });
// Check if annotation with the same ID already exists
const existingAnnotation = annotationList.find(
ann => ann.Id === annotation.Id
);
console.log(existingAnnotation, 'existing user');

if (
existingAnnotation &&
userEmail !== undefined &&
userEmail !== null &&
userEmail
) {
console.log('hi exitst shere');
return;
}

if (userEmail === undefined || userEmail === null || !userEmail) {
console.log('hiiiiiiii');
const selectedOption = options.find(
option => option.value === userRef.current?.value
);
if (annotation.getCustomData('stamp') === 'notary-stamp') {
return;
}
annotation.setCustomData('email', userRef.current?.value);
const rgbValues = selectedOption.color;
if (rgbValues && rgbValues.length === 3) {
const r = parseInt(rgbValues[0], 10);
const g = parseInt(rgbValues[1], 10);
const b = parseInt(rgbValues[2], 10);
annotation.StrokeColor = new Core.Annotations.Color(r, g, b);
annotation.StrokeThickness = 2;
annotation.TextColor = new Core.Annotations.Color(r, g, b);
}
// Additional logic to handle positioning adjustments
const coords = annotation.getRect();
const { x1, y1, x2 } = coords;
if (
x2 - x1 < 2 &&
toolbarNameRef.current !== 'toolbarGroup-Forms'
) {
annotation.setRect({ x1, y1, x2: x1 + 100, y2: y1 + 40 });
}
// annotationManager.updateAnnotation(annotation);
// annotationManager.redrawAnnotation(annotation);
}
}
}
});
};

const setEventListner = async () => {
if (instance.current) {
const { UI, Core } = instance.current;
const { annotationManager, Annotations } = Core;
}
};

// useEffect(() => {
// setEventListner();
// }, [instance?.current, selectedUser]);

const defaultCustomSettingUI = UI => {
UI.setFitMode('FitWidth');
UI.enableFeatures([UI.Feature.Initials]);
UI.disableFeatures([UI.Feature.Print, UI.Feature.Download]);
UI.setToolbarGroup('toolbarGroup-View');
};
const loadAnnotations = () => {
if (instance?.current) {
const { UI, Core } = instance.current;
const { documentViewer } = Core;

documentViewer.addEventListener('documentLoaded', async () => {
defaultCustomSettingUI(UI);

Core.annotationManager.setCurrentUser(user?.email);
if (!instance.Core) return;
const signatureTool =
new instance.Core.Annotations.SignatureWidgetAnnotation({});

signatureTool.setSigningMode(
Core.Tools.SignatureCreateTool.SigningModes.APPEARANCE
);

const originalCreateInnerElement =
signatureTool.prototype.createInnerElement;

signatureTool.prototype.createInnerElement = function () {
const ele = originalCreateInnerElement.apply(this, arguments);

UI.signSignatureWidget(ele);
ele.addEventListener('click', () => {
UI.closeElements('signatureModal');
UI.disableElements(['toolsOverlay']);
if (this.getCustomData('email') === user?.email) {
if (
this.getAssociatedSignatureAnnotation() ||
this.isSignedByAppearance()
) {
UI.enableElements('annotationPopup');
} else {
UI.openElements('signatureModal');
}
} else {
return;
}
});

return ele;
};
});
}
};
// load the doc when filePath changes
useEffect(() => {
const loadDocumentAndAnnotations = async () => {
if (instance.current && selectedDocument) {
const { Core, UI } = instance.current;
const { annotationManager, documentViewer } = Core;

await annotationManager.deleteAnnotations(
annotationManager.getAnnotationsList()
);

await documentViewer.setDocumentXFDFRetriever(async documentId => {
const rows = await fetchXPDFStrings(
parseS3SignedKey(selectedDocument?.document)
);
const annotations = rows?.data?.flatMap(a => a?.annotation || []);
const xfdfStrings = annotations
.map(annotation => annotation?.xfdf)
.filter(xfdf => xfdf !== undefined);
return xfdfStrings;
});

await UI.loadDocument(selectedDocument.document);
if (user?.userType !== 'notary') return;
const connectionStatus = getConnectionStatus();
if (connectionStatus !== 'CONNECTED') {
connect(
JSON.stringify({
type: 'joinRoom',
userType: user.userType,
appointmentId: appointmentsId,
participantId: user?.id,
})
);
}

const sendData = {
type: 'documentChanged',
appointmentId: appointmentsId,
documentId: parseS3SignedKey(selectedDocument?.document),
};
sendMessage(JSON.stringify(sendData));
}
};
loadDocumentAndAnnotations();
loadAnnotations();
}, [selectedDocument]);

const handleUserChange = event => {
userRef.current = event;
setSelectedUser(event);
};

const { control, register } = useForm({
mode: 'all',
});

return (
<Card className="lg:col-span-11 col-span-12" bodyClass="">
<div className="flex gap-4">
<span>{status} </span>
{user?.userType === 'notary' && (
<SelectField
options={options?.map(o => ({
value: o?.value,
label: o?.text,
color: o?.color,
}))}
onChange={handleUserChange}
name="select"
defaultValue={selectedUser}
control={control}
label="Select Role"
register={register}
className="mb-[10px] w-[15rem] "
isOptionIcon={true}
/>
)}
</div>{' '}
<div className="relative br-10 h-[100vh] " ref={viewer}></div>
</Card>
);
}

export default MeetingPdfWebViewer;
     
 
what is notes.io
 

Notes.io is a web-based application for taking notes. You can take your notes and share with others people. If you like taking long notes, notes.io is designed for you. To date, over 8,000,000,000 notes created and continuing...

With notes.io;

  • * You can take a note from anywhere and any device with internet connection.
  • * You can share the notes in social platforms (YouTube, Facebook, Twitter, instagram etc.).
  • * You can quickly share your contents without website, blog and e-mail.
  • * You don't need to create any Account to share a note. As you wish you can use quick, easy and best shortened notes with sms, websites, e-mail, or messaging services (WhatsApp, iMessage, Telegram, Signal).
  • * Notes.io has fabulous infrastructure design for a short link and allows you to share the note as an easy and understandable link.

Fast: Notes.io is built for speed and performance. You can take a notes quickly and browse your archive.

Easy: Notes.io doesn’t require installation. Just write and share note!

Short: Notes.io’s url just 8 character. You’ll get shorten link of your note when you want to share. (Ex: notes.io/q )

Free: Notes.io works for 12 years and has been free since the day it was started.


You immediately create your first note and start sharing with the ones you wish. If you want to contact us, you can use the following communication channels;


Email: [email protected]

Twitter: http://twitter.com/notesio

Instagram: http://instagram.com/notes.io

Facebook: http://facebook.com/notesio



Regards;
Notes.io Team

     
 
Shortened Note Link
 
 
Looding Image
 
     
 
Long File
 
 

For written notes was greater than 18KB Unable to shorten.

To be smaller than 18KB, please organize your notes, or sign in.