add shareModal and the ability to toggle share state

This commit is contained in:
MarconLP 2023-04-14 15:04:55 +02:00
parent fcb3ecbb14
commit c089fb7d47
No known key found for this signature in database
GPG key ID: A08A9C8B623F5EA5
5 changed files with 122 additions and 57 deletions

View file

@ -32,6 +32,12 @@ const config = {
port: '', port: '',
pathname: '/**', pathname: '/**',
}, },
{
protocol: 'https',
hostname: '*.imgur.com',
port: '',
pathname: '/**',
},
{ {
protocol: 'https', protocol: 'https',
hostname: '*.githubusercontent.com', hostname: '*.githubusercontent.com',

View file

@ -22,7 +22,13 @@ model Video {
title String title String
video_url String video_url String
userId String userId String
sharing Boolean @default(false)
deleteAfterExpiry Boolean @default(false)
shareExpiryAt DateTime?
linkShareSeo Boolean @default(false)
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
} }
// Necessary for Next auth // Necessary for Next auth

View file

@ -1,15 +1,36 @@
import { Dialog, Switch, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
import { Fragment, useState } from "react"; import { Fragment, useState } from "react";
import { ModernSwitch } from "~/components/ModernSwitch"; import { ModernSwitch } from "~/components/ModernSwitch";
import { api, type RouterOutputs } from "~/utils/api";
export function ShareModal() { interface Props {
video: RouterOutputs["video"]["get"];
}
export function ShareModal({ video }: Props) {
const utils = api.useContext();
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
const [sharing, setSharing] = useState<boolean>(false); const setSharingMutation = api.video.setSharing.useMutation({
const [es, sses] = useState<boolean>(false); onMutate: async ({ videoId, sharing }) => {
const [as, ssgs] = useState<boolean>(false); await utils.video.get.cancel();
const previousValue = utils.video.get.getData({ videoId });
if (previousValue) {
utils.video.get.setData({ videoId }, { ...previousValue, sharing });
}
return { previousValue };
},
});
const [s, ss] = useState<boolean>(false); const [linkCopied, setLinkCopied] = useState<boolean>(false);
const handleCopy = () => {
void navigator.clipboard.writeText(window.location.href);
setLinkCopied(true);
setTimeout(() => {
setLinkCopied(false);
}, 5000);
};
return ( return (
<> <>
@ -58,34 +79,52 @@ export function ShareModal() {
<span className="text-sm font-medium"> <span className="text-sm font-medium">
Share link with anyone Share link with anyone
</span> </span>
<ModernSwitch enabled={s} toggle={ss} /> <ModernSwitch
enabled={video.sharing}
toggle={() =>
setSharingMutation.mutate({
videoId: video.id,
sharing: !video.sharing,
})
}
/>
</div> </div>
<button className="my-2 h-8 w-full rounded-md bg-[#4169e1] text-sm font-medium text-white hover:bg-[#224fd7]"> {video.sharing ? (
Copy public link <>
<button
onClick={handleCopy}
className="my-2 h-8 w-full rounded-md bg-[#4169e1] text-sm font-medium text-white hover:bg-[#224fd7]"
>
{linkCopied ? "Copied!" : "Copy public link"}
</button> </button>
<div className="w-full border border-solid border-[#e9ebf0] bg-[#fafbfc] px-[15px] py-3 text-xs"> <div className="w-full border border-solid border-[#e9ebf0] bg-[#fafbfc] px-[15px] py-3 text-xs">
<div className="flex h-6 items-center justify-between"> {/*<div className="flex h-6 items-center justify-between">*/}
<span>Expire link</span> {/* <span>Expire link</span>*/}
<button className="h-6 rounded border border-solid border-[#d5d9df] bg-white px-[7px]"> {/* <button className="h-6 rounded border border-solid border-[#d5d9df] bg-white px-[7px] font-medium">*/}
Never expire {/* Never expire*/}
</button> {/* </button>*/}
</div> {/*</div>*/}
<div className="mt-3 flex h-6 items-center justify-between"> {/*<div className="mt-3 flex h-6 items-center justify-between">*/}
<span>Delete video when expired</span> {/* <span>Delete video when expired</span>*/}
<ModernSwitch enabled={s} toggle={ss} /> {/* <ModernSwitch enabled={s} toggle={ss} />*/}
</div> {/*</div>*/}
<div className="mt-3 flex h-6 items-center justify-between"> <div className="mt-3 flex h-6 items-center justify-between">
<span>Share link with search engines</span> <span>Share link with search engines</span>
<ModernSwitch enabled={s} toggle={ss} /> <ModernSwitch
</div> enabled={video.linkShareSeo}
<div className="mt-3 flex h-6 items-center justify-between"> toggle={() => console.log("test")}
<span>Embed code</span> />
<button className="h-6 rounded border border-solid border-[#d5d9df] bg-white px-[7px]">
Copy code
</button>
</div> </div>
{/*<div className="mt-3 flex h-6 items-center justify-between">*/}
{/* <span>Embed code</span>*/}
{/* <button className="h-6 rounded border border-solid border-[#d5d9df] bg-white px-[7px] font-medium">*/}
{/* Copy code*/}
{/* </button>*/}
{/*</div>*/}
</div> </div>
</>
) : null}
</div> </div>
</Dialog.Panel> </Dialog.Panel>
</Transition.Child> </Transition.Child>

View file

@ -21,7 +21,7 @@ const VideoList: NextPage = () => {
} }
); );
if (!isLoading && !video?.success) { if (!isLoading && !video) {
return ( return (
<div className="flex h-screen w-screen flex-col items-center justify-center"> <div className="flex h-screen w-screen flex-col items-center justify-center">
<span className="max-w-[80%] text-center text-2xl font-medium"> <span className="max-w-[80%] text-center text-2xl font-medium">
@ -50,29 +50,29 @@ const VideoList: NextPage = () => {
Personal Library Personal Library
</span> </span>
</Link> </Link>
<ShareModal /> {video ? <ShareModal video={video} /> : null}
</div> </div>
</div> </div>
<div className="flex h-full w-full grow flex-col items-center justify-start overflow-auto bg-[#fbfbfb]"> <div className="flex h-full w-full grow flex-col items-center justify-start overflow-auto bg-[#fbfbfb]">
<div className="flex aspect-video max-h-[627px] w-full justify-center bg-black 2xl:max-h-[1160px]"> <div className="flex aspect-video max-h-[627px] w-full justify-center bg-black 2xl:max-h-[1160px]">
{video?.video?.video_url && ( {video?.video_url && (
<ReactPlayer <ReactPlayer
width="100%" width="100%"
height="100%" height="100%"
controls={true} controls={true}
url={video.video?.video_url} url={video?.video_url}
/> />
)} )}
</div> </div>
<div className="mb-10 mt-4 w-full max-w-[1800px] pl-[24px]"> <div className="mb-10 mt-4 w-full max-w-[1800px] pl-[24px]">
<div> <div>
{video?.video?.title ? ( {video?.title ? (
<div className="mb-4 flex flex-col"> <div className="mb-4 flex flex-col">
<span className="text-[18px] text-lg font-medium"> <span className="text-[18px] text-lg font-medium">
{video?.video?.title} {video.title}
</span> </span>
<span className="text-[18px] text-sm text-gray-800"> <span className="text-[18px] text-sm text-gray-800">
{getTime(video?.video?.createdAt)} {getTime(video.createdAt)}
</span> </span>
</div> </div>
) : ( ) : (
@ -89,13 +89,14 @@ const VideoList: NextPage = () => {
<Image <Image
width={40} width={40}
height={40} height={40}
src={video?.video?.user?.image ?? ""} src={
video.user.image ??
"https://i.stack.imgur.com/dr5qp.jpg"
}
alt="profile photo" alt="profile photo"
/> />
</div> </div>
<span className="ml-3 font-medium"> <span className="ml-3 font-medium">{video.user.name}</span>
{video?.video?.user?.name}
</span>
</> </>
) : ( ) : (
<> <>

View file

@ -4,6 +4,7 @@ import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3"; import { GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";
import { env } from "~/env.mjs"; import { env } from "~/env.mjs";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { TRPCError } from "@trpc/server";
export const videoRouter = createTRPCRouter({ export const videoRouter = createTRPCRouter({
getAll: protectedProcedure.query(async ({ ctx }) => { getAll: protectedProcedure.query(async ({ ctx }) => {
@ -29,9 +30,7 @@ export const videoRouter = createTRPCRouter({
}); });
if (video?.userId !== ctx.session.user.id) { if (video?.userId !== ctx.session.user.id) {
return { throw new TRPCError({ code: "FORBIDDEN" });
success: false,
};
} }
const getObjectCommand = new GetObjectCommand({ const getObjectCommand = new GetObjectCommand({
@ -43,10 +42,7 @@ export const videoRouter = createTRPCRouter({
video.video_url = signedUrl; video.video_url = signedUrl;
return { return video;
success: true,
video,
};
}), }),
getUploadUrl: protectedProcedure getUploadUrl: protectedProcedure
.input(z.object({ key: z.string() })) .input(z.object({ key: z.string() }))
@ -77,4 +73,21 @@ export const videoRouter = createTRPCRouter({
signedUrl, signedUrl,
}; };
}), }),
setSharing: protectedProcedure
.input(z.object({ videoId: z.string(), sharing: z.boolean() }))
.mutation(async ({ ctx, input }) => {
const updateVideo = await ctx.prisma.video.update({
where: {
id: input.videoId,
},
data: {
sharing: input.sharing,
},
});
return {
success: true,
updateVideo,
};
}),
}); });