From ee4ca018d8436d16f3657d439abab20a87ba9f01 Mon Sep 17 00:00:00 2001 From: Mythie Date: Thu, 6 Apr 2023 23:16:20 +1000 Subject: [PATCH 1/8] fix: improve text insertion accuracy Previous inserted text would appear a little off center from where the user had selected which could cause some frustration. We improve upon this by updating the code responsible for centering the text to behave in a more accurate manner. From what I can tell it looks to be quite solid but could do with more rigorous testing with shorter and longer inputs. You can see the improved accuracy in action here: https://www.loom.com/share/1095fee7605c4790b8b30f573a04f0f0 --- packages/pdf/insertTextInPDF.ts | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/pdf/insertTextInPDF.ts b/packages/pdf/insertTextInPDF.ts index 83487e94b..b24a920c5 100644 --- a/packages/pdf/insertTextInPDF.ts +++ b/packages/pdf/insertTextInPDF.ts @@ -12,27 +12,36 @@ export async function insertTextInPDF( ): Promise { const fontBytes = fs.readFileSync("public/fonts/Qwigley-Regular.ttf"); - const existingPdfBytes = pdfAsBase64; + const pdfDoc = await PDFDocument.load(pdfAsBase64); - const pdfDoc = await PDFDocument.load(existingPdfBytes); pdfDoc.registerFontkit(fontkit); - const customFont = await pdfDoc.embedFont(fontBytes); - const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica); + + const font = await pdfDoc.embedFont(useHandwritingFont ? fontBytes : StandardFonts.Helvetica); const pages = pdfDoc.getPages(); const pdfPage = pages[page]; + const textSize = useHandwritingFont ? 50 : 15; - const textWidth = customFont.widthOfTextAtSize(text, textSize); - const textHeight = customFont.heightAtSize(textSize); + const textWidth = font.widthOfTextAtSize(text, textSize); + const textHeight = font.heightAtSize(textSize); const fieldSize = { width: 192, height: 64 }; - const invertedYPosition = pdfPage.getHeight() - positionY - fieldSize.height; + + // Because pdf-lib use a bottom-left coordinate system, we need to invert the y position + // we then center the text in the middle by adding half the height of the text + // plus the height of the field and divide the result by 2 + const invertedYPosition = + pdfPage.getHeight() - positionY - (fieldSize.height + textHeight / 2) / 2; + + // We center the text by adding the width of the field, subtracting the width of the text + // and dividing the result by 2 + const centeredXPosition = positionX + (fieldSize.width - textWidth) / 2; pdfPage.drawText(text, { - x: positionX, + x: centeredXPosition, y: invertedYPosition, size: textSize, - font: useHandwritingFont ? customFont : helveticaFont, color: rgb(0, 0, 0), + font, }); const pdfAsUint8Array = await pdfDoc.save(); From 593c317bf12e3443c4fe2900eb2d4b8b50c9b80a Mon Sep 17 00:00:00 2001 From: Saurav Gurhale Date: Thu, 6 Apr 2023 14:09:08 -0400 Subject: [PATCH 2/8] small fix for recipient-selector ListBox options must be unique --- apps/web/components/editor/recipient-selector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/components/editor/recipient-selector.tsx b/apps/web/components/editor/recipient-selector.tsx index d38bb2d1b..b5a0a494b 100644 --- a/apps/web/components/editor/recipient-selector.tsx +++ b/apps/web/components/editor/recipient-selector.tsx @@ -66,7 +66,7 @@ export default function RecipientSelector(props: any) { selected ? "font-semibold" : "font-normal", "ml-3 block truncate" )}> - {`${selectedRecipient?.name} <${selectedRecipient?.email}>`} + {`${recipient?.name} <${recipient?.email}>`} From e86d4cc71986c965755ef91230949fb0a30ef1c7 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 9 Apr 2023 12:26:48 +0000 Subject: [PATCH 3/8] Disable the edit and add signer button for completed documents --- apps/web/pages/documents.tsx | 1 + apps/web/pages/documents/[id]/recipients.tsx | 156 ++++++++++--------- packages/prisma/seed.ts | 2 +- 3 files changed, 85 insertions(+), 74 deletions(-) diff --git a/apps/web/pages/documents.tsx b/apps/web/pages/documents.tsx index b1ef1fce7..eaad896a5 100644 --- a/apps/web/pages/documents.tsx +++ b/apps/web/pages/documents.tsx @@ -291,6 +291,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => { event.stopPropagation(); router.push("/documents/" + document.id); }} + disabled={document.status === "COMPLETED"} /> { href={"/api/documents/" + props.document.id}> Download - - + {props.document.status !== DocumentStatus.COMPLETED && ( + <> + + + + )}
@@ -246,7 +252,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => { + className={`mt-3 inline-block flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800`}> Signed @@ -256,60 +262,64 @@ const RecipientsPage: NextPageWithLayout = (props: any) => { )}
-
- { - if (confirm("Resend this signing request?")) { - setLoading(true); - sendSigningRequests(props.document, [item.id]).finally(() => { - setLoading(false); - }); + {props.document.status !== DocumentStatus.COMPLETED && ( +
+ - Resend - - { - const removedItem = { ...fields }[index]; - remove(index); - deleteRecipient(item)?.catch((err) => { - append(removedItem); - }); - }} - className="group-hover:text-neon-dark group-hover:disabled:text-gray-400" - /> -
+ color="secondary" + className="my-auto mr-4 h-9" + onClick={() => { + if (confirm("Resend this signing request?")) { + setLoading(true); + sendSigningRequests(props.document, [item.id]).finally(() => { + setLoading(false); + }); + } + }}> + Resend +
+ { + const removedItem = { ...fields }[index]; + remove(index); + deleteRecipient(item)?.catch((err) => { + append(removedItem); + }); + }} + className="group-hover:text-neon-dark group-hover:disabled:text-gray-400" + /> +
+ )} ))} - + {props.document.status !== "COMPLETED" && ( + + )} diff --git a/packages/prisma/seed.ts b/packages/prisma/seed.ts index 42812331f..d686e02ac 100644 --- a/packages/prisma/seed.ts +++ b/packages/prisma/seed.ts @@ -24,7 +24,7 @@ async function createUser(userData: { email: string; password: string }) { async function main() { console.info("Start seeding..."); const password = "123456789"; - const email = "example6@documenso.com"; + const email = "example@documenso.com"; const user = await createUser({ email: email, password: await hashPassword(password), From 065efabb392fffb383a8231c980f97bccd877f8d Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 9 Apr 2023 12:29:31 +0000 Subject: [PATCH 4/8] Change wording on completed signers page --- apps/web/pages/documents/[id]/recipients.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/pages/documents/[id]/recipients.tsx b/apps/web/pages/documents/[id]/recipients.tsx index 4807c60eb..d786dc40d 100644 --- a/apps/web/pages/documents/[id]/recipients.tsx +++ b/apps/web/pages/documents/[id]/recipients.tsx @@ -124,7 +124,9 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {

Signers

- The people who will sign the document. + {props.document.status !== DocumentStatus.COMPLETED + ? "The people who will sign the document." + : "The people who signed the document."}

From 0ce66a7957f540d09cfeab51dae987ded9592061 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 9 Apr 2023 12:34:26 +0000 Subject: [PATCH 5/8] Redirect breadcrump link on completed to avoid editing --- apps/web/pages/documents/[id]/recipients.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/web/pages/documents/[id]/recipients.tsx b/apps/web/pages/documents/[id]/recipients.tsx index d786dc40d..ba86ba642 100644 --- a/apps/web/pages/documents/[id]/recipients.tsx +++ b/apps/web/pages/documents/[id]/recipients.tsx @@ -34,7 +34,10 @@ const RecipientsPage: NextPageWithLayout = (props: any) => { }, { title: props.document.title, - href: NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id, + href: + props.document.status !== DocumentStatus.COMPLETED + ? NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id + : NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id + "/recipients", }, { title: "Recipients", From 2a34cc26c65b52cd76fa301eaf351255afffaae9 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 9 Apr 2023 12:39:18 +0000 Subject: [PATCH 6/8] Replace empty string with fragments --- apps/web/pages/documents/[id]/recipients.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/pages/documents/[id]/recipients.tsx b/apps/web/pages/documents/[id]/recipients.tsx index ba86ba642..bf15aee21 100644 --- a/apps/web/pages/documents/[id]/recipients.tsx +++ b/apps/web/pages/documents/[id]/recipients.tsx @@ -227,7 +227,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => { Not Sent ) : ( - "" + <> )} {item.sendStatus === "SENT" && item.readStatus !== "OPENED" ? ( @@ -238,7 +238,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => { ) : ( - "" + <> )} {item.readStatus === "OPENED" && item.signingStatus === "NOT_SIGNED" ? ( @@ -251,19 +251,19 @@ const RecipientsPage: NextPageWithLayout = (props: any) => { ) : ( - "" + <> )} {item.signingStatus === "SIGNED" ? ( + className="mt-3 inline-block flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800"> Signed ) : ( - "" + <> )} From 6034e7a21ee17413bca91889a03b8981207156a3 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 9 Apr 2023 13:15:44 +0000 Subject: [PATCH 7/8] Send email notification to signers on document signing completion with signed document --- apps/web/pages/api/documents/[id]/sign.ts | 13 ++++++++++++- packages/lib/mail/sendSigningDoneMail.ts | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/web/pages/api/documents/[id]/sign.ts b/apps/web/pages/api/documents/[id]/sign.ts index ef68e1a78..537d7ee72 100644 --- a/apps/web/pages/api/documents/[id]/sign.ts +++ b/apps/web/pages/api/documents/[id]/sign.ts @@ -73,6 +73,13 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) { }, }); + const signedRecipients = await prisma.recipient.findMany({ + where: { + documentId: recipient.documentId, + signingStatus: SigningStatus.SIGNED, + }, + }); + // Don't check for inserted, because currently no "sign again" scenarios exist and // this is probably the expected behaviour in unclean states. const nonSignatureFields = await prisma.field.findMany({ @@ -126,7 +133,11 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) { }); document.document = documentWithInserts; - if (documentOwner) await sendSigningDoneMail(recipient, document, documentOwner); + if (documentOwner) await sendSigningDoneMail(document, documentOwner); + + for (const signer of signedRecipients) { + await sendSigningDoneMail(document, signer); + } } return res.status(200).end(); diff --git a/packages/lib/mail/sendSigningDoneMail.ts b/packages/lib/mail/sendSigningDoneMail.ts index 67c73868e..c7b35bf93 100644 --- a/packages/lib/mail/sendSigningDoneMail.ts +++ b/packages/lib/mail/sendSigningDoneMail.ts @@ -3,7 +3,7 @@ import { addDigitalSignature } from "@documenso/signing/addDigitalSignature"; import { sendMail } from "./sendMail"; import { Document as PrismaDocument } from "@prisma/client"; -export const sendSigningDoneMail = async (recipient: any, document: PrismaDocument, user: any) => { +export const sendSigningDoneMail = async (document: PrismaDocument, user: any) => { await sendMail( user.email, `Completed: "${document.title}"`, From e4e44b7f22167a4839767b7398aed8f5a53d9782 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Mon, 10 Apr 2023 01:34:20 +0000 Subject: [PATCH 8/8] Replace fragment with null --- apps/web/pages/documents/[id]/recipients.tsx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/apps/web/pages/documents/[id]/recipients.tsx b/apps/web/pages/documents/[id]/recipients.tsx index bf15aee21..fefa9a27a 100644 --- a/apps/web/pages/documents/[id]/recipients.tsx +++ b/apps/web/pages/documents/[id]/recipients.tsx @@ -226,9 +226,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => { className="mt-3 inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800"> Not Sent - ) : ( - <> - )} + ) : null} {item.sendStatus === "SENT" && item.readStatus !== "OPENED" ? ( { Sent - ) : ( - <> - )} + ) : null} {item.readStatus === "OPENED" && item.signingStatus === "NOT_SIGNED" ? ( { Seen - ) : ( - <> - )} + ) : null} {item.signingStatus === "SIGNED" ? ( { Signed - ) : ( - <> - )} + ) : null} {props.document.status !== DocumentStatus.COMPLETED && (