mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
fix: resolve issues with signing document stickiness
This commit is contained in:
@ -12,7 +12,7 @@ export type AdminSectionLayoutProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function AdminSectionLayout({ children }: AdminSectionLayoutProps) {
|
export default async function AdminSectionLayout({ children }: AdminSectionLayoutProps) {
|
||||||
const user = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
if (!isAdmin(user)) {
|
if (!isAdmin(user)) {
|
||||||
redirect('/documents');
|
redirect('/documents');
|
||||||
|
|||||||
@ -30,11 +30,11 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
|
|||||||
redirect('/documents');
|
redirect('/documents');
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
const document = await getDocumentById({
|
const document = await getDocumentById({
|
||||||
id: documentId,
|
id: documentId,
|
||||||
userId: session.id,
|
userId: user.id,
|
||||||
}).catch(() => null);
|
}).catch(() => null);
|
||||||
|
|
||||||
if (!document || !document.documentData) {
|
if (!document || !document.documentData) {
|
||||||
@ -50,11 +50,11 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
|
|||||||
const [recipients, fields] = await Promise.all([
|
const [recipients, fields] = await Promise.all([
|
||||||
await getRecipientsForDocument({
|
await getRecipientsForDocument({
|
||||||
documentId,
|
documentId,
|
||||||
userId: session.id,
|
userId: user.id,
|
||||||
}),
|
}),
|
||||||
await getFieldsForDocument({
|
await getFieldsForDocument({
|
||||||
documentId,
|
documentId,
|
||||||
userId: session.id,
|
userId: user.id,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
|
|||||||
<EditDocumentForm
|
<EditDocumentForm
|
||||||
className="mt-8"
|
className="mt-8"
|
||||||
document={document}
|
document={document}
|
||||||
user={session}
|
user={user}
|
||||||
recipients={recipients}
|
recipients={recipients}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
dataUrl={documentDataUrl}
|
dataUrl={documentDataUrl}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export type DocumentsPageProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function DocumentsPage({ searchParams = {} }: DocumentsPageProps) {
|
export default async function DocumentsPage({ searchParams = {} }: DocumentsPageProps) {
|
||||||
const user = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
const stats = await getStats({
|
const stats = await getStats({
|
||||||
user,
|
user,
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export default async function AuthenticatedDashboardLayout({
|
|||||||
redirect('/signin');
|
redirect('/signin');
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NextAuthProvider session={session}>
|
<NextAuthProvider session={session}>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { Button } from '@documenso/ui/primitives/button';
|
|||||||
import { LocaleDate } from '~/components/formatter/locale-date';
|
import { LocaleDate } from '~/components/formatter/locale-date';
|
||||||
|
|
||||||
export default async function BillingSettingsPage() {
|
export default async function BillingSettingsPage() {
|
||||||
const user = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
const isBillingEnabled = await getServerComponentFlag('app_billing');
|
const isBillingEnabled = await getServerComponentFlag('app_billing');
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-
|
|||||||
import { PasswordForm } from '~/components/forms/password';
|
import { PasswordForm } from '~/components/forms/password';
|
||||||
|
|
||||||
export default async function PasswordSettingsPage() {
|
export default async function PasswordSettingsPage() {
|
||||||
const user = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-
|
|||||||
import { ProfileForm } from '~/components/forms/profile';
|
import { ProfileForm } from '~/components/forms/profile';
|
||||||
|
|
||||||
export default async function ProfileSettingsPage() {
|
export default async function ProfileSettingsPage() {
|
||||||
const user = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -53,7 +53,7 @@ export default async function CompletedSigningPage({
|
|||||||
recipient.email;
|
recipient.email;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center pt-24 lg:pt-36 xl:pt-44">
|
<div className="-mx-4 flex max-w-[100vw] flex-col items-center overflow-x-hidden px-4 pt-24 md:-mx-8 md:px-8 lg:pt-36 xl:pt-44">
|
||||||
{/* Card with recipient */}
|
{/* Card with recipient */}
|
||||||
<SigningCard3D name={recipientName} signingCelebrationImage={signingCelebration} />
|
<SigningCard3D name={recipientName} signingCelebrationImage={signingCelebration} />
|
||||||
|
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent position="end">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Share</DialogTitle>
|
<DialogTitle>Share</DialogTitle>
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
|||||||
<span className="font-medium text-blue-400">@documenso</span>
|
<span className="font-medium text-blue-400">@documenso</span>
|
||||||
. Check it out!
|
. Check it out!
|
||||||
<span className="mt-2 block" />
|
<span className="mt-2 block" />
|
||||||
<span className="font-medium text-blue-400">
|
<span className="break-all font-medium text-blue-400">
|
||||||
{window.location.origin}/share/{shareLink?.slug || '...'}
|
{window.location.origin}/share/{shareLink?.slug || '...'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useMemo, useState } from 'react';
|
|||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
import { useSession } from 'next-auth/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token';
|
import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token';
|
||||||
@ -27,6 +28,7 @@ export type SigningFormProps = {
|
|||||||
|
|
||||||
export const SigningForm = ({ document, recipient, fields }: SigningFormProps) => {
|
export const SigningForm = ({ document, recipient, fields }: SigningFormProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { data: session } = useSession();
|
||||||
|
|
||||||
const { fullName, signature, setFullName, setSignature } = useRequiredSigningContext();
|
const { fullName, signature, setFullName, setSignature } = useRequiredSigningContext();
|
||||||
|
|
||||||
@ -60,7 +62,11 @@ export const SigningForm = ({ document, recipient, fields }: SigningFormProps) =
|
|||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className={cn(
|
className={cn(
|
||||||
'dark:bg-background border-border bg-widget sticky top-20 flex h-full max-h-[80rem] flex-col rounded-xl border px-4 py-6',
|
'dark:bg-background border-border bg-widget sticky flex h-full flex-col rounded-xl border px-4 py-6',
|
||||||
|
{
|
||||||
|
'top-20 max-h-[min(68rem,calc(100vh-6rem))]': session,
|
||||||
|
'top-4 max-h-[min(68rem,calc(100vh-2rem))]': !session,
|
||||||
|
},
|
||||||
)}
|
)}
|
||||||
onSubmit={handleSubmit(onFormSubmit)}
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -10,11 +10,11 @@ export type SigningLayoutProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function SigningLayout({ children }: SigningLayoutProps) {
|
export default async function SigningLayout({ children }: SigningLayoutProps) {
|
||||||
const user = await getServerComponentSession();
|
const { user, session } = await getServerComponentSession();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NextAuthProvider>
|
<NextAuthProvider session={session}>
|
||||||
<div className="min-h-screen overflow-hidden">
|
<div className="min-h-screen">
|
||||||
{user && <AuthenticatedHeader user={user} />}
|
{user && <AuthenticatedHeader user={user} />}
|
||||||
|
|
||||||
<main className="mb-8 mt-8 px-4 md:mb-12 md:mt-12 md:px-8">{children}</main>
|
<main className="mb-8 mt-8 px-4 md:mb-12 md:mt-12 md:px-8">{children}</main>
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
|
|||||||
.then((buffer) => Buffer.from(buffer).toString('base64'))
|
.then((buffer) => Buffer.from(buffer).toString('base64'))
|
||||||
.then((data) => `data:application/pdf;base64,${data}`);
|
.then((data) => `data:application/pdf;base64,${data}`);
|
||||||
|
|
||||||
const user = await getServerComponentSession();
|
const { user } = await getServerComponentSession();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
document.status === DocumentStatus.COMPLETED ||
|
document.status === DocumentStatus.COMPLETED ||
|
||||||
@ -62,7 +62,7 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SigningProvider email={recipient.email} fullName={recipient.name} signature={user?.signature}>
|
<SigningProvider email={recipient.email} fullName={recipient.name} signature={user?.signature}>
|
||||||
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
|
<div className="mx-auto w-full max-w-screen-xl">
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={document.title}>
|
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={document.title}>
|
||||||
{document.title}
|
{document.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { Button } from '@documenso/ui/primitives/button';
|
|||||||
import NotFoundPartial from '~/components/partials/not-found';
|
import NotFoundPartial from '~/components/partials/not-found';
|
||||||
|
|
||||||
export default async function NotFound() {
|
export default async function NotFound() {
|
||||||
const session = await getServerComponentSession();
|
const { session } = await getServerComponentSession();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NotFoundPartial>
|
<NotFoundPartial>
|
||||||
|
|||||||
@ -11,10 +11,10 @@ export type AddFieldsActionInput = TAddFieldsFormSchema & {
|
|||||||
export const addFields = async ({ documentId, fields }: AddFieldsActionInput) => {
|
export const addFields = async ({ documentId, fields }: AddFieldsActionInput) => {
|
||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
const { id: userId } = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
await setFieldsForDocument({
|
await setFieldsForDocument({
|
||||||
userId,
|
userId: user.id,
|
||||||
documentId,
|
documentId,
|
||||||
fields: fields.map((field) => ({
|
fields: fields.map((field) => ({
|
||||||
id: field.nativeId,
|
id: field.nativeId,
|
||||||
|
|||||||
@ -11,10 +11,10 @@ export type AddSignersActionInput = TAddSignersFormSchema & {
|
|||||||
export const addSigners = async ({ documentId, signers }: AddSignersActionInput) => {
|
export const addSigners = async ({ documentId, signers }: AddSignersActionInput) => {
|
||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
const { id: userId } = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
await setRecipientsForDocument({
|
await setRecipientsForDocument({
|
||||||
userId,
|
userId: user.id,
|
||||||
documentId,
|
documentId,
|
||||||
recipients: signers.map((signer) => ({
|
recipients: signers.map((signer) => ({
|
||||||
id: signer.nativeId,
|
id: signer.nativeId,
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export type CompleteDocumentActionInput = TAddSubjectFormSchema & {
|
|||||||
export const completeDocument = async ({ documentId, email }: CompleteDocumentActionInput) => {
|
export const completeDocument = async ({ documentId, email }: CompleteDocumentActionInput) => {
|
||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
const { id: userId } = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
if (email.message || email.subject) {
|
if (email.message || email.subject) {
|
||||||
await upsertDocumentMeta({
|
await upsertDocumentMeta({
|
||||||
@ -23,7 +23,7 @@ export const completeDocument = async ({ documentId, email }: CompleteDocumentAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
return await sendDocument({
|
return await sendDocument({
|
||||||
userId,
|
userId: user.id,
|
||||||
documentId,
|
documentId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export const getServerSession = async ({ req, res }: GetServerSessionOptions) =>
|
|||||||
const session = await getNextAuthServerSession(req, res, NEXT_AUTH_OPTIONS);
|
const session = await getNextAuthServerSession(req, res, NEXT_AUTH_OPTIONS);
|
||||||
|
|
||||||
if (!session || !session.user?.email) {
|
if (!session || !session.user?.email) {
|
||||||
return null;
|
return { user: null, session: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await prisma.user.findFirstOrThrow({
|
const user = await prisma.user.findFirstOrThrow({
|
||||||
@ -24,14 +24,14 @@ export const getServerSession = async ({ req, res }: GetServerSessionOptions) =>
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return user;
|
return { user, session };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getServerComponentSession = async () => {
|
export const getServerComponentSession = async () => {
|
||||||
const session = await getNextAuthServerSession(NEXT_AUTH_OPTIONS);
|
const session = await getNextAuthServerSession(NEXT_AUTH_OPTIONS);
|
||||||
|
|
||||||
if (!session || !session.user?.email) {
|
if (!session || !session.user?.email) {
|
||||||
return null;
|
return { user: null, session: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await prisma.user.findFirstOrThrow({
|
const user = await prisma.user.findFirstOrThrow({
|
||||||
@ -40,15 +40,15 @@ export const getServerComponentSession = async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return user;
|
return { user, session };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRequiredServerComponentSession = async () => {
|
export const getRequiredServerComponentSession = async () => {
|
||||||
const session = await getServerComponentSession();
|
const { user, session } = await getServerComponentSession();
|
||||||
|
|
||||||
if (!session) {
|
if (!user || !session) {
|
||||||
throw new Error('No session found');
|
throw new Error('No session found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return session;
|
return { user, session };
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import { alphaid } from '../id';
|
|||||||
export const getPresignPostUrl = async (fileName: string, contentType: string) => {
|
export const getPresignPostUrl = async (fileName: string, contentType: string) => {
|
||||||
const client = getS3Client();
|
const client = getS3Client();
|
||||||
|
|
||||||
const user = await getServerComponentSession();
|
const { user } = await getServerComponentSession();
|
||||||
|
|
||||||
// Get the basename and extension for the file
|
// Get the basename and extension for the file
|
||||||
const { name, ext } = path.parse(fileName);
|
const { name, ext } = path.parse(fileName);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { CreateNextContextOptions } from '@trpc/server/adapters/next';
|
|||||||
import { getServerSession } from '@documenso/lib/next-auth/get-server-session';
|
import { getServerSession } from '@documenso/lib/next-auth/get-server-session';
|
||||||
|
|
||||||
export const createTrpcContext = async ({ req, res }: CreateNextContextOptions) => {
|
export const createTrpcContext = async ({ req, res }: CreateNextContextOptions) => {
|
||||||
const session = await getServerSession({ req, res });
|
const { session, user } = await getServerSession({ req, res });
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return {
|
return {
|
||||||
@ -12,9 +12,16 @@ export const createTrpcContext = async ({ req, res }: CreateNextContextOptions)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return {
|
||||||
|
session: null,
|
||||||
|
user: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
session,
|
session,
|
||||||
user: session,
|
user,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,9 +11,19 @@ const Dialog = DialogPrimitive.Root;
|
|||||||
|
|
||||||
const DialogTrigger = DialogPrimitive.Trigger;
|
const DialogTrigger = DialogPrimitive.Trigger;
|
||||||
|
|
||||||
const DialogPortal = ({ className, children, ...props }: DialogPrimitive.DialogPortalProps) => (
|
const DialogPortal = ({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
position = 'start',
|
||||||
|
...props
|
||||||
|
}: DialogPrimitive.DialogPortalProps & { position?: 'start' | 'end' }) => (
|
||||||
<DialogPrimitive.Portal className={cn(className)} {...props}>
|
<DialogPrimitive.Portal className={cn(className)} {...props}>
|
||||||
<div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center">
|
<div
|
||||||
|
className={cn('fixed inset-0 z-50 flex justify-center sm:items-center', {
|
||||||
|
'items-start': position === 'start',
|
||||||
|
'items-end': position === 'end',
|
||||||
|
})}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</DialogPrimitive.Portal>
|
</DialogPrimitive.Portal>
|
||||||
@ -39,14 +49,14 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|||||||
|
|
||||||
const DialogContent = React.forwardRef<
|
const DialogContent = React.forwardRef<
|
||||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { position?: 'start' | 'end' }
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, children, position = 'start', ...props }, ref) => (
|
||||||
<DialogPortal>
|
<DialogPortal position={position}>
|
||||||
<DialogOverlay />
|
<DialogOverlay />
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-background animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-50 grid w-full gap-4 rounded-b-lg border p-6 shadow-lg sm:max-w-lg sm:rounded-lg',
|
'bg-background animate-in data-[state=open]:fade-in-90 sm:zoom-in-90 data-[state=open]:slide-in-from-bottom-10 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-50 grid w-full gap-4 rounded-b-lg border p-6 shadow-lg sm:max-w-lg sm:rounded-lg',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
Reference in New Issue
Block a user