diff --git a/apps/web/src/app/(dashboard)/documents/data-table-action-button.tsx b/apps/web/src/app/(dashboard)/documents/data-table-action-button.tsx index 72ac08915..17b577c13 100644 --- a/apps/web/src/app/(dashboard)/documents/data-table-action-button.tsx +++ b/apps/web/src/app/(dashboard)/documents/data-table-action-button.tsx @@ -2,12 +2,15 @@ import Link from 'next/link'; -import { Edit, Pencil, Share } from 'lucide-react'; +import { Download, Edit, Pencil } from 'lucide-react'; import { useSession } from 'next-auth/react'; import { match } from 'ts-pattern'; -import { Document, DocumentStatus, Recipient, SigningStatus, User } from '@documenso/prisma/client'; -import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button'; +import { getFile } from '@documenso/lib/universal/upload/get-file'; +import type { Document, Recipient, User } from '@documenso/prisma/client'; +import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; +import type { DocumentWithData } from '@documenso/prisma/types/document-with-data'; +import { trpc as trpcClient } from '@documenso/trpc/client'; import { Button } from '@documenso/ui/primitives/button'; export type DataTableActionButtonProps = { @@ -33,6 +36,41 @@ export const DataTableActionButton = ({ row }: DataTableActionButtonProps) => { const isComplete = row.status === DocumentStatus.COMPLETED; const isSigned = recipient?.signingStatus === SigningStatus.SIGNED; + const onDownloadClick = async () => { + let document: DocumentWithData | null = null; + + if (!recipient) { + document = await trpcClient.document.getDocumentById.query({ + id: row.id, + }); + } else { + document = await trpcClient.document.getDocumentByToken.query({ + token: recipient.token, + }); + } + + const documentData = document?.documentData; + + if (!documentData) { + return; + } + + const documentBytes = await getFile(documentData); + + const blob = new Blob([documentBytes], { + type: 'application/pdf', + }); + + const link = window.document.createElement('a'); + + link.href = window.URL.createObjectURL(blob); + link.download = row.title || 'document.pdf'; + + link.click(); + + window.URL.revokeObjectURL(link.href); + }; + return match({ isOwner, isRecipient, @@ -42,7 +80,7 @@ export const DataTableActionButton = ({ row }: DataTableActionButtonProps) => { isSigned, }) .with({ isOwner: true, isDraft: true }, () => ( - )) .with({ isRecipient: true, isPending: true, isSigned: false }, () => ( - )) - .otherwise(() => ( - ( - - )} - /> - )); + .with({ isPending: true, isSigned: true }, () => ( + + )) + .with({ isComplete: true }, () => ( + + )) + .otherwise(() =>
); }; diff --git a/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx b/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx index d1a7dcb5f..11636c016 100644 --- a/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx +++ b/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx @@ -147,12 +147,12 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) = ( e.preventDefault()}>
{loading ? : } - Share + Share Signing Card
)} diff --git a/apps/web/src/app/(dashboard)/documents/page.tsx b/apps/web/src/app/(dashboard)/documents/page.tsx index 4323be571..f38668fd9 100644 --- a/apps/web/src/app/(dashboard)/documents/page.tsx +++ b/apps/web/src/app/(dashboard)/documents/page.tsx @@ -80,7 +80,7 @@ export default async function DocumentsPage({ searchParams = {} }: DocumentsPage ].map((value) => ( diff --git a/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx b/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx index 8d993d74f..d8f0ecac8 100644 --- a/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx +++ b/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx @@ -3,15 +3,14 @@ import { NextResponse } from 'next/server'; import { P, match } from 'ts-pattern'; -import { Logo } from '~/components/branding/logo'; -import { ShareHandlerAPIResponse } from '~/pages/api/share'; +import type { ShareHandlerAPIResponse } from '~/pages/api/share'; export const runtime = 'edge'; -const CARD_OFFSET_TOP = 152; -const CARD_OFFSET_LEFT = 350; -const CARD_WIDTH = 500; -const CARD_HEIGHT = 250; +const CARD_OFFSET_TOP = 173; +const CARD_OFFSET_LEFT = 307; +const CARD_WIDTH = 590; +const CARD_HEIGHT = 337; const IMAGE_SIZE = { width: 1200, @@ -33,7 +32,7 @@ export async function GET(_request: Request, { params: { slug } }: SharePageOpen fetch(new URL('@documenso/assets/fonts/caveat-regular.ttf', import.meta.url)).then( async (res) => res.arrayBuffer(), ), - fetch(new URL('@documenso/assets/static/og-share-frame.png', import.meta.url)).then( + fetch(new URL('@documenso/assets/static/og-share-frame2.png', import.meta.url)).then( async (res) => res.arrayBuffer(), ), ]); @@ -72,11 +71,6 @@ export async function GET(_request: Request, { params: { slug } }: SharePageOpen {/* @ts-expect-error Lack of typing from ImageResponse */} og-share-frame -
- {/* @ts-expect-error Lack of typing from ImageResponse */} - -
- {signatureImage ? (

- {isRecipient - ? 'I just signed with Documenso and you can too!' - : 'I just sent a document with Documenso and you can too!'} + {isRecipient ? 'Document Signed!' : 'Document Sent!'}

diff --git a/apps/web/src/app/(share)/share/[slug]/page.tsx b/apps/web/src/app/(share)/share/[slug]/page.tsx index 51684d384..8e8fd7769 100644 --- a/apps/web/src/app/(share)/share/[slug]/page.tsx +++ b/apps/web/src/app/(share)/share/[slug]/page.tsx @@ -11,7 +11,7 @@ type SharePageProps = { export function generateMetadata({ params: { slug } }: SharePageProps) { return { title: 'Documenso - Share', - description: 'I just signed a document with Documenso!', + description: 'I just signed a document in style with Documenso!', openGraph: { title: 'Documenso - Join the open source signing revolution', description: 'I just signed with Documenso!', diff --git a/packages/assets/static/og-share-frame2.png b/packages/assets/static/og-share-frame2.png new file mode 100644 index 000000000..b9a65657d Binary files /dev/null and b/packages/assets/static/og-share-frame2.png differ diff --git a/packages/lib/server-only/share/get-recipient-or-sender-by-share-link-slug.ts b/packages/lib/server-only/share/get-recipient-or-sender-by-share-link-slug.ts index 423a97bc5..fee8eecc1 100644 --- a/packages/lib/server-only/share/get-recipient-or-sender-by-share-link-slug.ts +++ b/packages/lib/server-only/share/get-recipient-or-sender-by-share-link-slug.ts @@ -13,20 +13,6 @@ export const getRecipientOrSenderByShareLinkSlug = async ({ }, }); - const recipient = await prisma.recipient.findFirst({ - where: { - documentId, - email, - }, - include: { - Signature: true, - }, - }); - - if (recipient) { - return recipient; - } - const sender = await prisma.user.findFirst({ where: { Document: { some: { id: documentId } }, @@ -43,5 +29,19 @@ export const getRecipientOrSenderByShareLinkSlug = async ({ return sender; } + const recipient = await prisma.recipient.findFirst({ + where: { + documentId, + email, + }, + include: { + Signature: true, + }, + }); + + if (recipient) { + return recipient; + } + throw new Error('Recipient or sender not found'); }; diff --git a/packages/ui/components/document/document-share-button.tsx b/packages/ui/components/document/document-share-button.tsx index c243fd87a..5b6e9006a 100644 --- a/packages/ui/components/document/document-share-button.tsx +++ b/packages/ui/components/document/document-share-button.tsx @@ -1,8 +1,9 @@ 'use client'; -import React, { HTMLAttributes, useState } from 'react'; +import type { HTMLAttributes } from 'react'; +import React, { useState } from 'react'; -import { Copy, Share } from 'lucide-react'; +import { Copy, Sparkles } from 'lucide-react'; import { FaXTwitter } from 'react-icons/fa6'; import { useCopyShareLink } from '@documenso/lib/client-only/hooks/use-copy-share-link'; @@ -48,9 +49,11 @@ export const DocumentShareButton = ({ const { mutateAsync: createOrGetShareLink, data: shareLink, - isLoading, + isLoading: isCreatingOrGettingShareLink, } = trpc.shareLink.createOrGetShareLink.useMutation(); + const isLoading = isCreatingOrGettingShareLink || isCopyingShareLink; + const onOpenChange = (nextOpen: boolean) => { if (nextOpen) { void createOrGetShareLink({ @@ -95,7 +98,7 @@ export const DocumentShareButton = ({ window.open( generateTwitterIntent( - `I just ${token ? 'signed' : 'sent'} a document with @documenso. Check it out!`, + `I just ${token ? 'signed' : 'sent'} a document in style with @documenso. Check it out!`, `${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`, ), '_blank', @@ -108,31 +111,34 @@ export const DocumentShareButton = ({ e.stopPropagation()} asChild> {trigger?.({ - disabled: !token || !documentId, - loading: isLoading || isCopyingShareLink, + disabled: !documentId, + loading: isLoading, }) || ( )} - Share + Share your signing experience! - Share your signing experience! + + Don't worry, the document you signed or sent wont be shared; only your signing + experience is. Share your signing card and showcase your signature! +
- I just {token ? 'signed' : 'sent'} a document with{' '} + I just {token ? 'signed' : 'sent'} a document in style with{' '} @documenso . Check it out! @@ -144,9 +150,12 @@ export const DocumentShareButton = ({ {process.env.NEXT_PUBLIC_WEBAPP_URL}/share/{shareLink?.slug || '...'}
{shareLink?.slug && (
- +
+ -
-
- Or -
+
- -