Merge pull request #455 from documenso/fix/432-signee-doc-version-doesnt-have-sticky-signing-area

fix: resolve issues with signing document stickiness
This commit is contained in:
Lucas Smith
2023-09-28 17:50:04 +10:00
committed by GitHub
20 changed files with 66 additions and 43 deletions

View File

@ -12,7 +12,7 @@ export type AdminSectionLayoutProps = {
};
export default async function AdminSectionLayout({ children }: AdminSectionLayoutProps) {
const user = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
if (!isAdmin(user)) {
redirect('/documents');

View File

@ -30,11 +30,11 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
redirect('/documents');
}
const session = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
const document = await getDocumentById({
id: documentId,
userId: session.id,
userId: user.id,
}).catch(() => null);
if (!document || !document.documentData) {
@ -50,11 +50,11 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
const [recipients, fields] = await Promise.all([
await getRecipientsForDocument({
documentId,
userId: session.id,
userId: user.id,
}),
await getFieldsForDocument({
documentId,
userId: session.id,
userId: user.id,
}),
]);
@ -87,7 +87,7 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
<EditDocumentForm
className="mt-8"
document={document}
user={session}
user={user}
recipients={recipients}
fields={fields}
dataUrl={documentDataUrl}

View File

@ -25,7 +25,7 @@ export type DocumentsPageProps = {
};
export default async function DocumentsPage({ searchParams = {} }: DocumentsPageProps) {
const user = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
const stats = await getStats({
user,

View File

@ -24,7 +24,7 @@ export default async function AuthenticatedDashboardLayout({
redirect('/signin');
}
const user = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
return (
<NextAuthProvider session={session}>

View File

@ -12,7 +12,7 @@ import { Button } from '@documenso/ui/primitives/button';
import { LocaleDate } from '~/components/formatter/locale-date';
export default async function BillingSettingsPage() {
const user = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
const isBillingEnabled = await getServerComponentFlag('app_billing');

View File

@ -3,7 +3,7 @@ import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-
import { PasswordForm } from '~/components/forms/password';
export default async function PasswordSettingsPage() {
const user = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
return (
<div>

View File

@ -3,7 +3,7 @@ import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-
import { ProfileForm } from '~/components/forms/profile';
export default async function ProfileSettingsPage() {
const user = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
return (
<div>

View File

@ -53,7 +53,7 @@ export default async function CompletedSigningPage({
recipient.email;
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 */}
<SigningCard3D name={recipientName} signingCelebrationImage={signingCelebration} />

View File

@ -106,7 +106,7 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
</Button>
</DialogTrigger>
<DialogContent>
<DialogContent position="end">
<DialogHeader>
<DialogTitle>Share</DialogTitle>
@ -119,7 +119,7 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
<span className="font-medium text-blue-400">@documenso</span>
. Check it out!
<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 || '...'}
</span>
</div>

View File

@ -4,6 +4,7 @@ import { useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { useSession } from 'next-auth/react';
import { useForm } from 'react-hook-form';
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) => {
const router = useRouter();
const { data: session } = useSession();
const { fullName, signature, setFullName, setSignature } = useRequiredSigningContext();
@ -60,7 +62,11 @@ export const SigningForm = ({ document, recipient, fields }: SigningFormProps) =
return (
<form
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)}
>

View File

@ -10,11 +10,11 @@ export type SigningLayoutProps = {
};
export default async function SigningLayout({ children }: SigningLayoutProps) {
const user = await getServerComponentSession();
const { user, session } = await getServerComponentSession();
return (
<NextAuthProvider>
<div className="min-h-screen overflow-hidden">
<NextAuthProvider session={session}>
<div className="min-h-screen">
{user && <AuthenticatedHeader user={user} />}
<main className="mb-8 mt-8 px-4 md:mb-12 md:mt-12 md:px-8">{children}</main>

View File

@ -51,7 +51,7 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
.then((buffer) => Buffer.from(buffer).toString('base64'))
.then((data) => `data:application/pdf;base64,${data}`);
const user = await getServerComponentSession();
const { user } = await getServerComponentSession();
if (
document.status === DocumentStatus.COMPLETED ||
@ -62,7 +62,7 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
return (
<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}>
{document.title}
</h1>

View File

@ -6,7 +6,7 @@ import { Button } from '@documenso/ui/primitives/button';
import NotFoundPartial from '~/components/partials/not-found';
export default async function NotFound() {
const session = await getServerComponentSession();
const { session } = await getServerComponentSession();
return (
<NotFoundPartial>

View File

@ -11,10 +11,10 @@ export type AddFieldsActionInput = TAddFieldsFormSchema & {
export const addFields = async ({ documentId, fields }: AddFieldsActionInput) => {
'use server';
const { id: userId } = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
await setFieldsForDocument({
userId,
userId: user.id,
documentId,
fields: fields.map((field) => ({
id: field.nativeId,

View File

@ -11,10 +11,10 @@ export type AddSignersActionInput = TAddSignersFormSchema & {
export const addSigners = async ({ documentId, signers }: AddSignersActionInput) => {
'use server';
const { id: userId } = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
await setRecipientsForDocument({
userId,
userId: user.id,
documentId,
recipients: signers.map((signer) => ({
id: signer.nativeId,

View File

@ -12,7 +12,7 @@ export type CompleteDocumentActionInput = TAddSubjectFormSchema & {
export const completeDocument = async ({ documentId, email }: CompleteDocumentActionInput) => {
'use server';
const { id: userId } = await getRequiredServerComponentSession();
const { user } = await getRequiredServerComponentSession();
if (email.message || email.subject) {
await upsertDocumentMeta({
@ -23,7 +23,7 @@ export const completeDocument = async ({ documentId, email }: CompleteDocumentAc
}
return await sendDocument({
userId,
userId: user.id,
documentId,
});
};

View File

@ -15,7 +15,7 @@ export const getServerSession = async ({ req, res }: GetServerSessionOptions) =>
const session = await getNextAuthServerSession(req, res, NEXT_AUTH_OPTIONS);
if (!session || !session.user?.email) {
return null;
return { user: null, session: null };
}
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 () => {
const session = await getNextAuthServerSession(NEXT_AUTH_OPTIONS);
if (!session || !session.user?.email) {
return null;
return { user: null, session: null };
}
const user = await prisma.user.findFirstOrThrow({
@ -40,15 +40,15 @@ export const getServerComponentSession = async () => {
},
});
return user;
return { user, session };
};
export const getRequiredServerComponentSession = async () => {
const session = await getServerComponentSession();
const { user, session } = await getServerComponentSession();
if (!session) {
if (!user || !session) {
throw new Error('No session found');
}
return session;
return { user, session };
};

View File

@ -17,7 +17,7 @@ import { alphaid } from '../id';
export const getPresignPostUrl = async (fileName: string, contentType: string) => {
const client = getS3Client();
const user = await getServerComponentSession();
const { user } = await getServerComponentSession();
// Get the basename and extension for the file
const { name, ext } = path.parse(fileName);

View File

@ -3,7 +3,7 @@ import { CreateNextContextOptions } from '@trpc/server/adapters/next';
import { getServerSession } from '@documenso/lib/next-auth/get-server-session';
export const createTrpcContext = async ({ req, res }: CreateNextContextOptions) => {
const session = await getServerSession({ req, res });
const { session, user } = await getServerSession({ req, res });
if (!session) {
return {
@ -12,9 +12,16 @@ export const createTrpcContext = async ({ req, res }: CreateNextContextOptions)
};
}
if (!user) {
return {
session: null,
user: null,
};
}
return {
session,
user: session,
user,
};
};

View File

@ -11,9 +11,19 @@ const Dialog = DialogPrimitive.Root;
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}>
<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}
</div>
</DialogPrimitive.Portal>
@ -39,14 +49,14 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { position?: 'start' | 'end' }
>(({ className, children, position = 'start', ...props }, ref) => (
<DialogPortal position={position}>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
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,
)}
{...props}