Merge branch 'feat/refresh' into feat/admin-ui-manage-instance

This commit is contained in:
Timur Ercan
2023-10-13 16:44:44 +02:00
committed by GitHub
27 changed files with 147 additions and 96 deletions

View File

@ -6,13 +6,12 @@ import { Edit, Pencil, Share } from 'lucide-react';
import { useSession } from 'next-auth/react';
import { match } from 'ts-pattern';
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
import { Document, DocumentStatus, Recipient, SigningStatus, User } from '@documenso/prisma/client';
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
export type DataTableActionButtonProps = {
row: Document & {
User: Pick<User, 'id' | 'name' | 'email'>;
@ -47,7 +46,7 @@ export const DataTableActionButton = ({ row }: DataTableActionButtonProps) => {
documentId: row.id,
});
await copyToClipboard(`${window.location.origin}/share/${slug}`).catch(() => null);
await copyToClipboard(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`).catch(() => null);
toast({
title: 'Copied to clipboard',

View File

@ -18,6 +18,7 @@ import {
} from 'lucide-react';
import { useSession } from 'next-auth/react';
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
import { getFile } from '@documenso/lib/universal/upload/get-file';
import { Document, DocumentStatus, Recipient, User } from '@documenso/prisma/client';
import { DocumentWithData } from '@documenso/prisma/types/document-with-data';
@ -32,8 +33,6 @@ import {
} from '@documenso/ui/primitives/dropdown-menu';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
import { DeleteDraftDocumentDialog } from './delete-draft-document-dialog';
export type DataTableActionDropdownProps = {
@ -61,7 +60,7 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
const isOwner = row.User.id === session.user.id;
// const isRecipient = !!recipient;
// const isDraft = row.status === DocumentStatus.DRAFT;
const isDraft = row.status === DocumentStatus.DRAFT;
// const isPending = row.status === DocumentStatus.PENDING;
const isComplete = row.status === DocumentStatus.COMPLETED;
// const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
@ -73,7 +72,7 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
documentId: row.id,
});
await copyToClipboard(`${window.location.origin}/share/${slug}`).catch(() => null);
await copyToClipboard(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`).catch(() => null);
toast({
title: 'Copied to clipboard',
@ -166,7 +165,7 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
Resend
</DropdownMenuItem>
<DropdownMenuItem onClick={onShareClick}>
<DropdownMenuItem disabled={isDraft} onClick={onShareClick}>
{isCreatingShareLink ? (
<Loader className="mr-2 h-4 w-4" />
) : (

View File

@ -9,12 +9,11 @@ import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-f
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
import { DocumentStatus, FieldType } from '@documenso/prisma/client';
import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button';
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
import { SigningCard3D } from '@documenso/ui/components/signing-card';
import signingCelebration from '~/assets/signing-celebration.png';
import { ShareButton } from './share-button';
export type CompletedSigningPageProps = {
params: {
token?: string;
@ -89,7 +88,7 @@ export default async function CompletedSigningPage({
))}
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
<ShareButton documentId={document.id} token={recipient.token} />
<DocumentShareButton documentId={document.id} token={recipient.token} />
<DocumentDownloadButton
className="flex-1"

View File

@ -1,147 +0,0 @@
'use client';
import { HTMLAttributes, useState } from 'react';
import { Copy, Share } from 'lucide-react';
import { FaXTwitter } from 'react-icons/fa6';
import { generateTwitterIntent } from '@documenso/lib/universal/generate-twitter-intent';
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@documenso/ui/primitives/dialog';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
export type ShareButtonProps = HTMLAttributes<HTMLButtonElement> & {
token: string;
documentId: number;
};
export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
const { toast } = useToast();
const [, copyToClipboard] = useCopyToClipboard();
const [isOpen, setIsOpen] = useState(false);
const {
mutateAsync: createOrGetShareLink,
data: shareLink,
isLoading,
} = trpc.shareLink.createOrGetShareLink.useMutation();
const onOpenChange = (nextOpen: boolean) => {
if (nextOpen) {
void createOrGetShareLink({
token,
documentId,
});
}
setIsOpen(nextOpen);
};
const onCopyClick = async () => {
let { slug = '' } = shareLink || {};
if (!slug) {
const result = await createOrGetShareLink({
token,
documentId,
});
slug = result.slug;
}
await copyToClipboard(`${window.location.origin}/share/${slug}`).catch(() => null);
toast({
title: 'Copied to clipboard',
description: 'The sharing link has been copied to your clipboard.',
});
setIsOpen(false);
};
const onTweetClick = async () => {
let { slug = '' } = shareLink || {};
if (!slug) {
const result = await createOrGetShareLink({
token,
documentId,
});
slug = result.slug;
}
window.open(
generateTwitterIntent(
`I just ${token ? 'signed' : 'sent'} a document with @documenso. Check it out!`,
`${window.location.origin}/share/${slug}`,
),
'_blank',
);
setIsOpen(false);
};
return (
<Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogTrigger asChild>
<Button
variant="outline"
disabled={!token || !documentId}
className="flex-1"
loading={isLoading}
>
{!isLoading && <Share className="mr-2 h-5 w-5" />}
Share
</Button>
</DialogTrigger>
<DialogContent position="end">
<DialogHeader>
<DialogTitle>Share</DialogTitle>
<DialogDescription className="mt-4">Share your signing experience!</DialogDescription>
</DialogHeader>
<div className="flex w-full flex-col">
<div className="rounded-md border p-4">
I just {token ? 'signed' : 'sent'} a document with{' '}
<span className="font-medium text-blue-400">@documenso</span>
. Check it out!
<span className="mt-2 block" />
<span className="break-all font-medium text-blue-400">
{window.location.origin}/share/{shareLink?.slug || '...'}
</span>
</div>
<Button variant="outline" className="mt-4" onClick={onTweetClick}>
<FaXTwitter className="mr-2 h-4 w-4" />
Tweet
</Button>
<div className="relative flex items-center justify-center gap-x-4 py-4 text-xs uppercase">
<div className="bg-border h-px flex-1" />
<span className="text-muted-foreground bg-transparent">Or</span>
<div className="bg-border h-px flex-1" />
</div>
<Button variant="outline" onClick={onCopyClick}>
<Copy className="mr-2 h-4 w-4" />
Copy Link
</Button>
</div>
</DialogContent>
</Dialog>
);
};

View File

@ -1,6 +1,6 @@
'use client';
import { useMemo, useState, useTransition } from 'react';
import { useEffect, useMemo, useState, useTransition } from 'react';
import { useRouter } from 'next/navigation';
@ -48,6 +48,7 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
const [showSignatureModal, setShowSignatureModal] = useState(false);
const [localSignature, setLocalSignature] = useState<string | null>(null);
const [isLocalSignatureSet, setIsLocalSignatureSet] = useState(false);
const state = useMemo<SignatureFieldState>(() => {
if (!field.inserted) {
@ -61,9 +62,16 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
return 'signed-text';
}, [field.inserted, signature?.signatureImageAsBase64]);
useEffect(() => {
if (!showSignatureModal && !isLocalSignatureSet) {
setLocalSignature(null);
}
}, [showSignatureModal, isLocalSignatureSet]);
const onSign = async (source: 'local' | 'provider' = 'provider') => {
try {
if (!providedSignature && !localSignature) {
setIsLocalSignatureSet(false);
setShowSignatureModal(true);
return;
}
@ -178,6 +186,7 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
disabled={!localSignature}
onClick={() => {
setShowSignatureModal(false);
setIsLocalSignatureSet(true);
void onSign('local');
}}
>

View File

@ -117,7 +117,8 @@ export const ProfileForm = ({ className, user }: ProfileFormProps) => {
name="signature"
render={({ field: { onChange } }) => (
<SignaturePad
className="h-44 w-full rounded-lg border bg-white backdrop-blur-sm dark:border-[#e2d7c5] dark:bg-[#fcf8ee]"
className="h-44 w-full"
containerClassName="rounded-lg border bg-background"
defaultValue={user.signature ?? undefined}
onChange={(v) => onChange(v ?? '')}
/>

View File

@ -147,7 +147,8 @@ export const SignUpForm = ({ className }: SignUpFormProps) => {
name="signature"
render={({ field: { onChange } }) => (
<SignaturePad
className="mt-2 h-36 w-full rounded-lg border bg-white dark:border-[#e2d7c5] dark:bg-[#fcf8ee]"
className="h-36 w-full"
containerClassName="mt-2 rounded-lg border bg-background"
onChange={(v) => onChange(v ?? '')}
/>
)}

View File

@ -1,28 +0,0 @@
import { useState } from 'react';
export type CopiedValue = string | null;
export type CopyFn = (_text: string) => Promise<boolean>;
export function useCopyToClipboard(): [CopiedValue, CopyFn] {
const [copiedText, setCopiedText] = useState<CopiedValue>(null);
const copy: CopyFn = async (text) => {
if (!navigator?.clipboard) {
console.warn('Clipboard not supported');
return false;
}
// Try to save to clipboard then save it in the state if worked
try {
await navigator.clipboard.writeText(text);
setCopiedText(text);
return true;
} catch (error) {
console.warn('Copy failed', error);
setCopiedText(null);
return false;
}
};
return [copiedText, copy];
}