diff --git a/app/(content)/(home)/home/page.tsx b/app/(content)/(home)/home/page.tsx index eb3fad8382e6982a1feb83a9fa0d88501a458b74..3152776a2d11dc773e98ee25cd1a5282e4f61e3c 100644 --- a/app/(content)/(home)/home/page.tsx +++ b/app/(content)/(home)/home/page.tsx @@ -1,6 +1,5 @@ -import { CreateGweet } from "@/components/create-gweet"; -import { Gweets } from "@/components/gweets"; - +import { CreateGweet } from "@/components/create-gweet/components/create-gweet"; +import { Gweets } from "@/components/gweets/components/gweets"; export default async function HomePage() { return ( diff --git a/app/(content)/(user)/[userid]/status/[id]/page.tsx b/app/(content)/(user)/[userid]/status/[id]/page.tsx index bb63ac57e8ee81ad7aecfd91916b8c8bb0adc888..6860c7066d593c8513c6f12307d85bf86c625455 100644 --- a/app/(content)/(user)/[userid]/status/[id]/page.tsx +++ b/app/(content)/(user)/[userid]/status/[id]/page.tsx @@ -1,4 +1,4 @@ -import { GweetDetails } from "@/components/gweets"; +import { GweetDetails } from "@/components/gweets/components/gweet-details"; import { GweetHeader } from "@/components/layout"; export default async function GweetDetailPage() { diff --git a/app/api/gweets/[id]/route.ts b/app/api/gweets/[id]/route.ts index d75022fa3349d905073cfc39a219774e81899a3a..be74cbb2f647e78903eb32e75f36022734037b66 100644 --- a/app/api/gweets/[id]/route.ts +++ b/app/api/gweets/[id]/route.ts @@ -58,7 +58,6 @@ export async function GET(request: Request, { params }: { params: { id: string } media: true, }, }, - allQuotes: { include: { likes: true, @@ -75,6 +74,7 @@ export async function GET(request: Request, { params }: { params: { id: string } createdAt: "desc", }, }, + allComments: true, }, }); diff --git a/app/api/gweets/route.ts b/app/api/gweets/route.ts index 60143cc82225996cf8b35937111858166f10f6b0..57c4d7cbe8f7317b82fe4671e0f52508bc4379dc 100644 --- a/app/api/gweets/route.ts +++ b/app/api/gweets/route.ts @@ -2,6 +2,7 @@ import { NextResponse } from "next/server"; import { z } from "zod"; import { db } from "@/lib/db"; +import { utapi } from "uploadthing/server"; // get gweets export async function GET(request: Request) { @@ -87,7 +88,7 @@ export async function GET(request: Request) { // create gweet export async function POST(request: Request) { const gweet = await request.json(); - + console.log(gweet) const gweetSchema = z .object({ content: z.string().min(1).max(280), @@ -144,6 +145,16 @@ export async function DELETE(request: Request) { } try { + const checkMedia = await db.media.findMany({ + where: { + gweetId: id, + }, + }); + + if (checkMedia.length > 0) { + await utapi.deleteFiles(checkMedia.map((media) => media.key)); + } + await db.gweet.delete({ where: { id, diff --git a/app/api/media/route.ts b/app/api/media/route.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f98d377a5c639772d8a82a1997f73442a4ce247 --- /dev/null +++ b/app/api/media/route.ts @@ -0,0 +1,34 @@ +import { NextResponse } from "next/server"; +import { z } from "zod"; + +import { db } from "@/lib/db"; + +export async function POST(request: Request) { + const { media } = await request.json(); + + const mediaSchema = z + .object({ + gweetId: z.string().nullable().optional(), + url: z.string(), + key: z.string(), + type: z.string(), + }) + .strict(); + + const zod = mediaSchema.safeParse(media); + + if (!zod.success) { + return NextResponse.json({ error: zod.error }, { status: 400 }); + } + + try { + await db.media.create({ + data: { + ...media, + }, + }); + return NextResponse.json({ message: "Media created successfully" }, { status: 200 }); + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} \ No newline at end of file diff --git a/app/api/uploadthing/core.ts b/app/api/uploadthing/core.ts new file mode 100644 index 0000000000000000000000000000000000000000..ddd9152dc3ba66b282bf273f0c985b5591961ed1 --- /dev/null +++ b/app/api/uploadthing/core.ts @@ -0,0 +1,22 @@ +import { getCurrentUser } from "@/lib/session"; +import { createUploadthing, type FileRouter } from "uploadthing/next"; + +const f = createUploadthing(); + +export const ourFileRouter = { + imageUploader: f({ image: { maxFileSize: "4MB" } }) + .middleware(async ({ req }) => { + const user = await getCurrentUser(); + + if (!user) throw new Error("Unauthorized"); + + return { userId: user.id }; + }) + .onUploadComplete(async ({ metadata, file }) => { + console.log("Upload complete for userId:", metadata.userId); + + console.log("file url", file.url); + }), +} satisfies FileRouter; + +export type OurFileRouter = typeof ourFileRouter; \ No newline at end of file diff --git a/app/api/uploadthing/route.ts b/app/api/uploadthing/route.ts new file mode 100644 index 0000000000000000000000000000000000000000..d092e55a4cc395f5c35b2078d4f42200d8029951 --- /dev/null +++ b/app/api/uploadthing/route.ts @@ -0,0 +1,7 @@ +import { createNextRouteHandler } from "uploadthing/next"; + +import { ourFileRouter } from "./core"; + +export const { GET, POST } = createNextRouteHandler({ + router: ourFileRouter, +}); \ No newline at end of file diff --git a/components/create-gweet/api/post-gweet.ts b/components/create-gweet/api/post-gweet.ts index 799dc8a223c737404478b18ecce6a664a5794c00..13ef6fa2dba409d9d55e2ef85ad8362c61848c3f 100644 --- a/components/create-gweet/api/post-gweet.ts +++ b/components/create-gweet/api/post-gweet.ts @@ -1,22 +1,22 @@ import { postHashtags, retrieveHashtagsFromGweet } from "@/components/trends"; -import { postMedia } from "./post-media"; +import { uploadFiles } from "@/lib/uploadthing"; export const postGweet = async ({ content, files, - userId, + authorId, replyToGweetId, quoteGweetId, }: { content: string; files: File[]; - userId: string; + authorId: string; replyToGweetId?: string | null; quoteGweetId?: string | null; }) => { const gweet = { content, - userId, + authorId, ...(replyToGweetId && { replyToGweetId }), ...(quoteGweetId && { quoteGweetId }), }; @@ -28,7 +28,22 @@ export const postGweet = async ({ }).then((result) => result.json()) if (files.length > 0) { - await postMedia({ files, gweet_id: data.id }); + const gweet_id = data.id; + files.forEach(async (file) => { + const [res] = await uploadFiles({ files: [file], endpoint: 'imageUploader' }) + + const media = { + ...(gweet_id && { gweet_id }), + url: res.fileUrl, + key: res.fileKey, + type: "image", + }; + + await fetch('/api/media', { + method: 'POST', + body: JSON.stringify(media) + }) + }) } const hashtags = retrieveHashtagsFromGweet(content); diff --git a/components/create-gweet/api/post-media.ts b/components/create-gweet/api/post-media.ts deleted file mode 100644 index 7e6676be83d8601a521893f222c146d5d6537841..0000000000000000000000000000000000000000 --- a/components/create-gweet/api/post-media.ts +++ /dev/null @@ -1,60 +0,0 @@ -import dbmedia from "@/lib/db-media"; -import { createId } from '@paralleldrive/cuid2'; - -export const postMedia = async ({ - files, - gweet_id, -}: { - files: File[]; - gweet_id?: string; -}) => { - - try { - files.forEach(async (file) => { - const imagePath = createId(); - - const { error } = await dbmedia.storage - .from("images") - .upload(`image-${imagePath}`, file); - - if (error) { - console.log("error", error); - throw new Error("Failed to upload image"); - } else { - const { data: mediaUrl } = dbmedia.storage - .from("images") - .getPublicUrl(`image-${imagePath}`); - - const media = { - ...(gweet_id && { gweet_id }), - url: mediaUrl?.publicUrl, - type: "image", - }; - - await fetch('/api/media', { - method: 'POST', - body: JSON.stringify(media) - }) - } - }); - - return true; - } catch (error: any) { - if (error.response) { - // The request was made and the server responded with a status code - // that falls out of the range of 2xx - console.log(error.response.data); - console.log(error.response.status); - console.log(error.response.headers); - } else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - console.log(error.request); - } else { - // Something happened in setting up the request that triggered an Error - console.log("Error", error.message); - } - console.log(error.config); - } -}; diff --git a/components/create-gweet/components/create-gweet-wrapper.tsx b/components/create-gweet/components/create-gweet-wrapper.tsx index 2c36409e082a5b7793f53b6d0665d510b50ffc77..1a6cb12190b091cb91dab0ac9ac80c06df1f2c8b 100644 --- a/components/create-gweet/components/create-gweet-wrapper.tsx +++ b/components/create-gweet/components/create-gweet-wrapper.tsx @@ -12,10 +12,10 @@ export const CreateGweetWrapper = ({ const [isComment, setIsComment] = useState(true); return ( - <div className="relative border-b border-border"> + <div className=""> <CreateGweet replyToGweetId={replyToGweetId} - placeholder="Gweet your reply" + placeholder="Gweet your reply..." isComment={isComment} /> {isComment && ( @@ -23,8 +23,7 @@ export const CreateGweetWrapper = ({ onClick={() => { setIsComment(false); }} - className="absolute top-0 right-0 w-full h-full z-1 pointer-events-auto" - ></button> + /> )} </div> ); diff --git a/components/create-gweet/components/create-gweet.tsx b/components/create-gweet/components/create-gweet.tsx index fc7b35924d283b4a6288f77afabf48e923331969..4049658cf1ef381d03396d0657c693c4b0a00d0d 100644 --- a/components/create-gweet/components/create-gweet.tsx +++ b/components/create-gweet/components/create-gweet.tsx @@ -20,10 +20,10 @@ import { import { Textarea } from "@/components/ui/textarea"; import { toast } from "@/components/ui/use-toast"; -import { IGweet } from "@/components/gweets"; -import { Icons } from "@/components/icons"; +import { IGweet } from "@/components/gweets/types"; import { UserAvatar } from "@/components/user-avatar"; +import { Icons } from "@/components/icons"; import { Card } from "@/components/ui/card"; import { useCreateGweet } from "../hooks/use-create-gweet"; import { IChosenImages } from "../types"; @@ -67,7 +67,7 @@ export const CreateGweet = ({ mutation.mutate({ content: data.gweet, files: [], - userId: session?.user?.id, + authorId: session?.user?.id, replyToGweetId, quoteGweetId: quoted_gweet?.id || null, }) @@ -166,45 +166,7 @@ export const CreateGweet = ({ disabled={isGweetLoading || !session.user} {...field} /> - <input - className="hidden" - type="file" - onChange={(e) => chooseImage(e, setChosenImages)} - ref={imageUploadRef} - /> </FormControl> - <div className={`grid object-cover grid-cols- - ${chosenImages.length === 1 ? "1" - : chosenImages.length === 2 ? "2 space-x-3" - : chosenImages.length === 3 ? "3 space-x-3 space-y-3" - : chosenImages.length === 4 ? "4 space-x-3 space-y-3" - : "" - }`} - > - {chosenImages.map((image, i) => { - return ( - <div key={i} className="relative max-h-[700px] overflow-hidden"> - <Button - size="sm" - onClick={() => { - setChosenImages( - chosenImages.filter((img, j) => j !== i), - ); - }} - > - <Icons.close /> - </Button> - <Image - src={image.url as string} - alt="gweet image" - width={1000} - height={1000} - /> - </div> - ); - })} - {/* {quoted_gweet && <QuotedGweet gweet={quoted_gweet} />} */} - </div> {!isComment ? <FormDescription className="pt-3"> Your gweets will be public, and everyone can see them. @@ -212,6 +174,49 @@ export const CreateGweet = ({ : null } <FormMessage /> + + <input + className="hidden w-full" + type="file" + onChange={(e) => chooseImage(e, setChosenImages)} + ref={imageUploadRef} + /> + {chosenImages.length > 0 && ( + <div className={`grid object-cover h-[680px] + ${chosenImages.length === 1 ? "grid-cols-1" + : chosenImages.length === 2 ? "grid-cols-2 gap-3" + : chosenImages.length === 3 || 4 ? "grid-cols-2 grid-rows-2 gap-3" + : "" + }`} + > + {chosenImages.map((image, i) => { + const isFirstImage = chosenImages.length === 3 && i === 0; + return ( + <Card key={i} className={`relative max-h-[680px] overflow-hidden ${isFirstImage ? "row-span-2" : ""}`}> + <Button + size="icon" + variant="secondary" + className="rounded-full absolute top-1 right-1 z-50" + onClick={() => { + setChosenImages( + chosenImages.filter((img, j) => j !== i), + ); + }} + > + <Icons.close /> + </Button> + <Image + src={image.url as string} + alt="gweet image" + fill + className="object-cover rounded-lg" + /> + </Card> + ); + })} + </div> + )} + {/* {quoted_gweet && <QuotedGweet gweet={quoted_gweet} />} */} </div> </FormItem> )} diff --git a/components/create-gweet/hooks/use-create-gweet.ts b/components/create-gweet/hooks/use-create-gweet.ts index acec8cbe259539694cc8790fa53b2a8a8c00150a..43ba0510ccdc4fe370dd214d3acad91840d1d60e 100644 --- a/components/create-gweet/hooks/use-create-gweet.ts +++ b/components/create-gweet/hooks/use-create-gweet.ts @@ -10,21 +10,21 @@ export const useCreateGweet = () => { ({ content, files, - userId, + authorId, replyToGweetId, quoteGweetId, }: { content: string; files: File[]; - userId: string; + authorId: string; replyToGweetId?: string | null; quoteGweetId?: string | null; }) => { return postGweet({ content, files, - userId, + authorId, replyToGweetId, quoteGweetId, }); diff --git a/components/create-gweet/index.ts b/components/create-gweet/index.ts deleted file mode 100644 index 4d334ccfda7c35e5d02b3fef4c0c15b9d8af4280..0000000000000000000000000000000000000000 --- a/components/create-gweet/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./api/post-media"; -export * from "./components/create-gweet"; -export * from "./components/create-gweet-wrapper"; -export * from "./types"; diff --git a/components/gweets/api/delete-media.ts b/components/gweets/api/delete-media.ts deleted file mode 100644 index 20f2d3a7e50832771d878a49593523f6b820ff76..0000000000000000000000000000000000000000 --- a/components/gweets/api/delete-media.ts +++ /dev/null @@ -1,29 +0,0 @@ -import dbmedia from "@/lib/db-media"; - -export const deleteMedia = async (media: string[]) => { - try { - const { error } = await dbmedia.storage.from("images").remove(media); - - if (error) { - throw new Error("Failed to delete media"); - } - - } catch (error: any) { - if (error.response) { - // The request was made and the server responded with a status code - // that falls out of the range of 2xx - console.log(error.response.data); - console.log(error.response.status); - console.log(error.response.headers); - } else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - console.log(error.request); - } else { - // Something happened in setting up the request that triggered an Error - console.log("Error", error.message); - } - console.log(error.config); - } -}; \ No newline at end of file diff --git a/components/gweets/components/actions/comment-button.tsx b/components/gweets/components/actions/comment-button.tsx index 258395263c03b717539048e5b41561e23c8bb702..7797c74c174e5e529fa5637351b89b6a35ef02e3 100644 --- a/components/gweets/components/actions/comment-button.tsx +++ b/components/gweets/components/actions/comment-button.tsx @@ -5,17 +5,15 @@ import { Icons } from "@/components/icons"; export const CommentButton = ({ gweet, - showStats = false, }: { gweet: IGweet; - showStats: boolean; }) => { return ( <Button variant="ghost" size="lg" className="px-6 py-3 hover:bg-sky-800"> <Icons.messagecircle className="h-5 w-5" /> <span className="pl-2"> - {showStats && gweet?.comments?.length > 0 && ( - <span className="">{gweet?.comments?.length}</span> + {gweet?.allComments?.length > 0 && ( + <span className="">{gweet?.allComments?.length}</span> )} </span> </Button> diff --git a/components/gweets/components/actions/like-button.tsx b/components/gweets/components/actions/like-button.tsx index 2c0f192acaafc47c764a5c7fb697bb4fb5e3f0ec..be6a3a47b4ba642b903b7f05e966a7c3821590cf 100644 --- a/components/gweets/components/actions/like-button.tsx +++ b/components/gweets/components/actions/like-button.tsx @@ -7,11 +7,9 @@ import { IGweet } from "../../types"; export const LikeButton = ({ gweet, - showStats = false, }: { gweet?: IGweet; smallIcons?: boolean; - showStats?: boolean; }) => { const { data: session } = useSession(); const hasLiked = gweet?.likes?.some( @@ -40,7 +38,7 @@ export const LikeButton = ({ } <span className="pl-2"> - {showStats && gweet?.likes && gweet?.likes?.length > 0 && ( + {gweet?.likes && gweet?.likes?.length > 0 && ( <span className="">{gweet?.likes?.length}</span> )} </span> diff --git a/components/gweets/components/actions/regweet-button.tsx b/components/gweets/components/actions/regweet-button.tsx index d1abfd461cb48d46b6b7070a990e4c00ffe89c22..76c9ca3d5ef99f95bf8b533793390040647bb11f 100644 --- a/components/gweets/components/actions/regweet-button.tsx +++ b/components/gweets/components/actions/regweet-button.tsx @@ -27,7 +27,7 @@ export const RegweetButton = ({ gweet }: { gweet: IGweet }) => { variant="ghost" size="lg" className="px-6 py-3 hover:bg-green-800" > {hasRegweeted ? - <Icons.regweet className="h-5 w-5 fill-green-600 text-green-600" /> + <Icons.regweet className="h-5 w-5 text-green-600" /> : <Icons.regweet className="h-5 w-5" /> } diff --git a/components/gweets/components/comments.tsx b/components/gweets/components/comments.tsx index 8cbc59d40ba8c1bc6ce126a83805f044b8567cbc..969ed9a1a09d9b44b374128754a40ecb07d9a233 100644 --- a/components/gweets/components/comments.tsx +++ b/components/gweets/components/comments.tsx @@ -29,14 +29,12 @@ export const Comments = ({ gweetId }: { gweetId: string }) => { } return ( - <div className=""> - <InfiniteGweets - gweets={comments} - fetchNextPage={fetchNextPage} - hasNextPage={hasNextPage} - isFetchingNextPage={isFetchingNextPage} - isSuccess={isSuccess} - /> - </div> + <InfiniteGweets + gweets={comments} + fetchNextPage={fetchNextPage} + hasNextPage={hasNextPage} + isFetchingNextPage={isFetchingNextPage} + isSuccess={isSuccess} + /> ); -}; +}; \ No newline at end of file diff --git a/components/gweets/components/gweet-actions.tsx b/components/gweets/components/gweet-actions.tsx index e8589cdaf3f3dc56555041cb22fdee98f33a4bea..0d2d3bc921d7695d1dd6b40c9a66171b621d9e26 100644 --- a/components/gweets/components/gweet-actions.tsx +++ b/components/gweets/components/gweet-actions.tsx @@ -6,16 +6,14 @@ import { RegweetButton } from "./actions/regweet-button"; export const GweetActions = ({ gweet, - showStats = false, }: { gweet: IGweet; - showStats?: boolean | undefined; }) => { return ( <div className="space-x-2"> - <CommentButton gweet={gweet} showStats={showStats} /> + <CommentButton gweet={gweet} /> <RegweetButton gweet={gweet} /> - <LikeButton gweet={gweet} smallIcons={false} showStats={showStats} /> + <LikeButton gweet={gweet} smallIcons={false} /> </div> ); }; \ No newline at end of file diff --git a/components/gweets/components/gweet-author.tsx b/components/gweets/components/gweet-author.tsx index fb00c45aa5fa6498988a03db624a93728ea420a3..c0868040d8f261d973f94864b83999c99aa8edd0 100644 --- a/components/gweets/components/gweet-author.tsx +++ b/components/gweets/components/gweet-author.tsx @@ -1,5 +1,6 @@ -import { GweetOptions, IGweet } from "@/components/gweets"; import { UserAvatar } from "@/components/user-avatar"; +import { IGweet } from "../types"; +import { GweetOptions } from "./gweet-options"; export const GweetAuthor = ({ gweet }: { gweet: IGweet }) => { return ( @@ -9,8 +10,8 @@ export const GweetAuthor = ({ gweet }: { gweet: IGweet }) => { className="h-10 w-10" /> - <div> - <span className="font-bold mr-2">{gweet.author.name}</span> + <div className="flex flex-col ml-3"> + <span className="font-bold">{gweet.author.name}</span> <span className="text-sky-500 text-sm">@{gweet.author.username}</span> </div> diff --git a/components/gweets/components/gweet-creation-date.tsx b/components/gweets/components/gweet-creation-date.tsx index 9c5919733d0491b49c04a2fd8c7f94b14b137b9b..e787feb0d2187ab2f62386633f298635d909aa6e 100644 --- a/components/gweets/components/gweet-creation-date.tsx +++ b/components/gweets/components/gweet-creation-date.tsx @@ -2,7 +2,7 @@ import dayjs from "dayjs"; export const GweetCreationDate = ({ date }: { date: Date }) => { return ( - <div> + <div className="space-x-2"> <span>{dayjs(date).format(`h:mm A`)}</span> <span>·</span> <span>{dayjs(date).format(`MMM D, YYYY`)}</span> diff --git a/components/gweets/components/gweet-details.tsx b/components/gweets/components/gweet-details.tsx index 53d75fc2bfab9942ef6dc8e96d42e75210a5ee14..c04e824830faf8617df87d58757109ffa416b447 100644 --- a/components/gweets/components/gweet-details.tsx +++ b/components/gweets/components/gweet-details.tsx @@ -1,9 +1,10 @@ "use client" -import { CreateGweetWrapper } from "@/components/create-gweet"; +import { CreateGweetWrapper } from "@/components/create-gweet/components/create-gweet-wrapper"; import { Icons } from "@/components/icons"; import { TryAgain } from "@/components/try-again"; import { Card } from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; import Image from "next/image"; import { usePathname } from "next/navigation"; import { useGweet } from "../hooks/use-gweet"; @@ -33,13 +34,13 @@ export const GweetDetails = () => { return ( <Card className="w-full h-full mt-12 p-2 xl:p-4 "> - <div className="flex flex-col space-y-2"> + <div className="flex flex-col space-y-2 p-3"> <GweetAuthor gweet={gweet} /> {/* TODO needs handling of all gweets above and under the gweet */} - <div> - {gweet.content && <h1>{gweet.content}</h1>} + <div className="flex flex-col space-y-1"> + {gweet.content && <h1 className="mt-2">{gweet.content}</h1>} {gweet.media && gweet.media.length > 0 && ( <div className={`grid object-cover grid-cols- @@ -68,11 +69,22 @@ export const GweetDetails = () => { {/* {gweet?.quotedGweet && <QuotedGweet gweet={gweet?.quotedGweet} />} */} </div> - <GweetCreationDate date={gweet.createdAt} /> - <GweetActions gweet={gweet} /> + <div className="w-full"> + <div className="flex flex-col items-center flex-wrap md:flex-row space-y-3 md:space-y-0"> + <div className="flex-grow text-muted-foreground"> + <GweetCreationDate date={gweet.createdAt} /> + </div> + <div className="md:items-end"> + <GweetActions gweet={gweet} /> + </div> + </div> + </div> + + </div> <CreateGweetWrapper replyToGweetId={gweet?.id} /> + <Separator className=" h-1 mb-3" /> <Comments gweetId={gweet?.id} /> </Card> ); diff --git a/components/gweets/components/gweet-options.tsx b/components/gweets/components/gweet-options.tsx index c975c341a032cead257e8f475250fd7962a62fca..8078ab598ae28234e4786557f147799c50d7ab7c 100644 --- a/components/gweets/components/gweet-options.tsx +++ b/components/gweets/components/gweet-options.tsx @@ -1,6 +1,5 @@ "use client"; -import { IGweet } from "@/components/gweets"; import { Icons } from "@/components/icons"; import { Button } from "@/components/ui/button"; import { @@ -14,6 +13,7 @@ import { toast } from "@/components/ui/use-toast"; import { env } from "@/env.mjs"; import { useSession } from "next-auth/react"; import { useRef, useState } from "react"; +import { IGweet } from "../types"; import { DeleteGweetModal } from "./delete-gweet-modal"; export const GweetOptions = ({ gweet }: { gweet: IGweet }) => { diff --git a/components/gweets/components/gweet.tsx b/components/gweets/components/gweet.tsx index 6c538da72919f6cdfab3742b272f09b538d6dd7f..d966ee60c9f2855c650a69e3c67ef228d73b30d4 100644 --- a/components/gweets/components/gweet.tsx +++ b/components/gweets/components/gweet.tsx @@ -1,6 +1,6 @@ -import { Button } from "@/components/ui/button"; +import { buttonVariants } from "@/components/ui/button"; import { UserAvatar } from "@/components/user-avatar"; -import { formatTimeElapsed } from "@/lib/utils"; +import { cn, formatTimeElapsed } from "@/lib/utils"; import { useRouter } from "next/navigation"; import { IGweet } from "../types"; import { GweetActions } from "./gweet-actions"; @@ -10,11 +10,10 @@ export const Gweet = ({ gweet }: { gweet: IGweet }) => { const router = useRouter(); return ( - <Button - variant="ghost" + <div tabIndex={0} onClick={() => router.push(`/${gweet.author.username}/status/${gweet.id}`)} - className="flex h-auto w-full text-left" + className={cn(buttonVariants({ variant: "ghost" }), "flex h-auto w-full text-left cursor-pointer")} > <UserAvatar user={{ username: gweet.author.username, image: gweet.author.image }} @@ -54,9 +53,9 @@ export const Gweet = ({ gweet }: { gweet: IGweet }) => { )} </div> <div className="flex justify-end" > - <GweetActions gweet={gweet} showStats={true} /> + <GweetActions gweet={gweet} /> </div> </div> - </Button> + </div> ); }; \ No newline at end of file diff --git a/components/gweets/hooks/use-delete-gweet.ts b/components/gweets/hooks/use-delete-gweet.ts index b74b21f70deb3120715f251717e306ab519d1605..9fbfabc50bf0c921a9b8da521b9ea08e8686db4b 100644 --- a/components/gweets/hooks/use-delete-gweet.ts +++ b/components/gweets/hooks/use-delete-gweet.ts @@ -5,17 +5,14 @@ import { deleteGweet } from "../api/delete-gweet"; export const useDeleteGweet = () => { const queryClient = useQueryClient(); - return useMutation( - ({ gweetId }: { gweetId: string }) => { - return deleteGweet(gweetId); + return useMutation(({ gweetId }: { gweetId: string }) => { + return deleteGweet(gweetId); + }, { + onSuccess: () => { + queryClient.invalidateQueries(["gweets"]); }, - { - onSuccess: () => { - queryClient.invalidateQueries(["gweets"]); - }, - onError: (error) => { - console.log(error); - }, + onError: (error) => { + console.log(error); }, - ); -}; + }); +}; \ No newline at end of file diff --git a/components/gweets/index.ts b/components/gweets/index.ts deleted file mode 100644 index fb59a417b41449a937b135ee69004a2cfacc86a6..0000000000000000000000000000000000000000 --- a/components/gweets/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from "./components/gweet"; -export * from "./components/gweet-details"; -export * from "./components/gweet-options"; -// export * from "./components/gweet-quotes"; -export * from "./components/gweets"; -export * from "./components/infinite-gweets"; -// export * from "./components/inspect-gweet-image-modal"; -// export * from "./components/quoted-gweet"; -export * from "./hooks/use-gweets"; -export * from "./types"; diff --git a/components/gweets/types/index.ts b/components/gweets/types/index.ts index ca430e7325b7776d6a404449c63296ff65a6a46d..29e8cd6bd0d7110bdef7cc06d3d15eb348b6c152 100644 --- a/components/gweets/types/index.ts +++ b/components/gweets/types/index.ts @@ -1,6 +1,6 @@ import { Gweet, Like, Media, Regweet } from "@prisma/client"; -import { IUser } from "@/components/profile"; +import { IUser } from "@/components/profile/types"; export interface IFeed { id: number; @@ -12,8 +12,8 @@ export interface IGweet extends Gweet { likes: ILike[]; media: IMedia[]; regweets: IRegweet[]; - quotes: IGweet[]; - comments: IGweet[]; + allQuotes: IGweet[]; + allComments: IGweet[]; } export interface ILike extends Like { diff --git a/components/profile/index.ts b/components/profile/index.ts deleted file mode 100644 index eea524d655708d7561b53642ce62e241627eece6..0000000000000000000000000000000000000000 --- a/components/profile/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./types"; diff --git a/components/profile/types/index.ts b/components/profile/types/index.ts index 93dddedd93e8c2a7784961bfec661279f2615762..0e493c971e6051ff1dfdf4d6a895268dbc8b00cf 100644 --- a/components/profile/types/index.ts +++ b/components/profile/types/index.ts @@ -1,6 +1,6 @@ import { Follows, Like, User } from "@prisma/client"; -import { IGweet } from "@/components/gweets"; +import { IGweet } from "@/components/gweets/types"; export interface IUser extends User { gweets: IGweet[]; diff --git a/env.mjs b/env.mjs index 2e2b1ee7d6497494bc6d5e17907b273e20825892..f2ba50c268786a06d88998fcb2749adc6c3ec88c 100644 --- a/env.mjs +++ b/env.mjs @@ -9,6 +9,8 @@ export const env = createEnv({ NEXTAUTH_URL: z.string().url().optional(), NEXTAUTH_SECRET: z.string().min(1), TWITCH_CLIENT_ID: z.string().min(1), + UPLOADTHING_SECRET: z.string().min(1), + UPLOADTHING_APP_ID: z.string().min(1), TWITCH_CLIENT_SECRET: z.string().min(1), TWITCH_AUTH_BASE_URL: z.string().url().optional(), IGDB_BASE_URL: z.string().url().optional(), @@ -16,8 +18,6 @@ export const env = createEnv({ }, client: { NEXT_PUBLIC_APP_URL: z.string().min(1), - NEXT_PUBLIC_SUPABASE_URL: z.string().min(1), - NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1), }, runtimeEnv: { DATABASE_URL: process.env.DATABASE_URL, @@ -26,8 +26,8 @@ export const env = createEnv({ NEXTAUTH_URL: process.env.NEXTAUTH_URL, NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL, - NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL, - NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, + UPLOADTHING_SECRET: process.env.UPLOADTHING_SECRET, + UPLOADTHING_APP_ID: process.env.UPLOADTHING_APP_ID, TWITCH_CLIENT_ID: process.env.TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET: process.env.TWITCH_CLIENT_SECRET, TWITCH_AUTH_BASE_URL: process.env.TWITCH_AUTH_BASE_URL, diff --git a/lib/db-media.ts b/lib/db-media.ts deleted file mode 100644 index d08c18ca1f9e18d2a508f3f60da0a12d9cc1de28..0000000000000000000000000000000000000000 --- a/lib/db-media.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createClient } from "@supabase/supabase-js"; - -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string; -const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY as string; -const supabase = createClient(supabaseUrl, supabaseKey) - -const dbmedia = supabase - -export default dbmedia \ No newline at end of file diff --git a/lib/uploadthing.ts b/lib/uploadthing.ts new file mode 100644 index 0000000000000000000000000000000000000000..24fa466446c0345b00345a3fd095b4c9e0b605e7 --- /dev/null +++ b/lib/uploadthing.ts @@ -0,0 +1,10 @@ +import { generateComponents } from "@uploadthing/react"; + +import { generateReactHelpers } from '@uploadthing/react/hooks'; + +import type { OurFileRouter } from "@/app/api/uploadthing/core"; + +export const { UploadButton, UploadDropzone, Uploader } = + generateComponents<OurFileRouter>(); + +export const { uploadFiles } = generateReactHelpers<OurFileRouter>() \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 84a56ec586d99bd4342316dd36cd08185b68246a..b5be777e3e322085ab95f3ed70aadd3d27fa9ada 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "dependencies": { "@auth/prisma-adapter": "^1.0.0", "@hookform/resolvers": "^3.1.1", - "@paralleldrive/cuid2": "^2.2.1", "@prisma/client": "^4.16.1", "@radix-ui/react-aspect-ratio": "^1.0.3", "@radix-ui/react-avatar": "^1.0.3", @@ -23,11 +22,11 @@ "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.4", - "@supabase/supabase-js": "^2.26.0", - "@t3-oss/env-nextjs": "^0.4.1", - "@tanstack/react-query": "^4.29.15", + "@t3-oss/env-nextjs": "^0.6.0", + "@tanstack/react-query": "^4.29.17", + "@uploadthing/react": "^5.0.0", "bcrypt": "^5.1.0", - "class-variance-authority": "^0.6.0", + "class-variance-authority": "^0.6.1", "clsx": "^1.2.1", "dayjs": "^1.11.8", "lucide-react": "^0.252.0", @@ -37,11 +36,13 @@ "normalize-diacritics": "^4.0.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-dropzone": "^14.2.3", "react-hook-form": "^7.45.0", "react-infinite-scroll-component": "^6.1.0", "react-intersection-observer": "^9.5.1", "tailwind-merge": "^1.13.2", "tailwindcss-animate": "^1.0.6", + "uploadthing": "^5.0.0", "zod": "^3.21.4" }, "devDependencies": { @@ -451,17 +452,6 @@ "node": ">= 10" } }, - "node_modules/@noble/hashes": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", - "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -502,14 +492,6 @@ "url": "https://github.com/sponsors/panva" } }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.1.tgz", - "integrity": "sha512-GJhHYlMhyT2gWemDL7BGMWfTNhspJKkikLKh9wAy3z4GTTINvTYALkUd+eGQK7aLeVkVzPuSA0VCT3H5eEWbbw==", - "dependencies": { - "@noble/hashes": "^1.1.5" - } - }, "node_modules/@pkgr/utils": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz", @@ -1415,61 +1397,6 @@ "integrity": "sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==", "dev": true }, - "node_modules/@supabase/functions-js": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.1.2.tgz", - "integrity": "sha512-QCR6pwJs9exCl37bmpMisUd6mf+0SUBJ6mUpiAjEkSJ/+xW8TCuO14bvkWHADd5hElJK9MxNlMQXxSA4DRz9nQ==", - "dependencies": { - "cross-fetch": "^3.1.5" - } - }, - "node_modules/@supabase/gotrue-js": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.31.0.tgz", - "integrity": "sha512-YcwlbbNfedlue/HVIXtYBb4fuOrs29gNOTl6AmyxPp4zryRxzFvslVN9kmLDBRUAVU9fnPJh2bgOR3chRjJX5w==", - "dependencies": { - "cross-fetch": "^3.1.5" - } - }, - "node_modules/@supabase/postgrest-js": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.7.1.tgz", - "integrity": "sha512-xPRYLaZrkLbXNlzmHW6Wtf9hmcBLjjI5xUz2zj8oE2hgXGaYoZBBkpN9bmW9i17Z1f6Ujxa942AqK439XOA36A==", - "dependencies": { - "cross-fetch": "^3.1.5" - } - }, - "node_modules/@supabase/realtime-js": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.7.3.tgz", - "integrity": "sha512-c7TzL81sx2kqyxsxcDduJcHL9KJdCOoKimGP6lQSqiZKX42ATlBZpWbyy9KFGFBjAP4nyopMf5JhPi2ZH9jyNw==", - "dependencies": { - "@types/phoenix": "^1.5.4", - "@types/websocket": "^1.0.3", - "websocket": "^1.0.34" - } - }, - "node_modules/@supabase/storage-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.5.1.tgz", - "integrity": "sha512-nkR0fQA9ScAtIKA3vNoPEqbZv1k5B5HVRYEvRWdlP6mUpFphM9TwPL2jZ/ztNGMTG5xT6SrHr+H7Ykz8qzbhjw==", - "dependencies": { - "cross-fetch": "^3.1.5" - } - }, - "node_modules/@supabase/supabase-js": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.26.0.tgz", - "integrity": "sha512-RXmTPTobaYAwkSobadHZmEVLmzX3SGrtRZIGfLWnLv92VzBRrjuXn0a+bJqKl50GUzsyqPA+j5pod7EwMkcH5A==", - "dependencies": { - "@supabase/functions-js": "^2.1.0", - "@supabase/gotrue-js": "^2.31.0", - "@supabase/postgrest-js": "^1.7.0", - "@supabase/realtime-js": "^2.7.3", - "@supabase/storage-js": "^2.5.1", - "cross-fetch": "^3.1.5" - } - }, "node_modules/@swc/helpers": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", @@ -1479,20 +1406,20 @@ } }, "node_modules/@t3-oss/env-core": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@t3-oss/env-core/-/env-core-0.4.1.tgz", - "integrity": "sha512-JZI8vxlHwtiZO7OYS3qSX9Ngt7UcdqsugLwhBwx7UVi5wp1PtRo2tzMyNoBEGbfHdmkd2QU9IbvYjqtaLUA7TQ==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@t3-oss/env-core/-/env-core-0.6.0.tgz", + "integrity": "sha512-3FkPAba069WRZVVab/sB1m3eSGn/rZeypx5k+sWEu1d+k0OQdRDnvFS+7MtxYgqVrwaRk3b7yVnX2dgSPVmWPQ==", "peerDependencies": { "typescript": ">=4.7.2", "zod": "^3.0.0" } }, "node_modules/@t3-oss/env-nextjs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@t3-oss/env-nextjs/-/env-nextjs-0.4.1.tgz", - "integrity": "sha512-lDbewJvZwOW7bFwHzdKtAH4+YcVTvGU7UEJfHwlb1RqVe9ejPrKn6ax2OGv/nFji1JXGKecKYHz3Xkpru6TFbw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@t3-oss/env-nextjs/-/env-nextjs-0.6.0.tgz", + "integrity": "sha512-SpzcGNIbUYcQw4zPPFeRJqCC1560zL7QmB0puIqOnuCsmykPkqHPX+n9CNZLXVQerboHzfvb7Kd+jAdouk72Vw==", "dependencies": { - "@t3-oss/env-core": "0.4.1" + "@t3-oss/env-core": "0.6.0" }, "peerDependencies": { "typescript": ">=4.7.2", @@ -1563,12 +1490,8 @@ "node_modules/@types/node": { "version": "20.3.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", - "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" - }, - "node_modules/@types/phoenix": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.0.tgz", - "integrity": "sha512-qwfpsHmFuhAS/dVd4uBIraMxRd56vwBUYQGZ6GpXnFuM2XMRFJbIyruFKKlW2daQliuYZwe0qfn/UjFCDKic5g==" + "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==", + "dev": true }, "node_modules/@types/prop-types": { "version": "15.7.5", @@ -1602,14 +1525,6 @@ "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", "devOptional": true }, - "node_modules/@types/websocket": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.5.tgz", - "integrity": "sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/parser": { "version": "5.60.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", @@ -1711,6 +1626,38 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@uploadthing/mime-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@uploadthing/mime-types/-/mime-types-0.2.0.tgz", + "integrity": "sha512-nRcdyM622+GYT9SdaJIVLknqoc8i7krItinfuBPtvmPc+UNGDJ+pMkr4AatYp88cEc4iYtPlN8flJvZ3dNtgrg==" + }, + "node_modules/@uploadthing/react": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@uploadthing/react/-/react-5.0.0.tgz", + "integrity": "sha512-rWqL9V/gyCa+rWB0XNPSUEdy0w9Mlx/I/LxQkmzC2NCAwIUeub11pecdMwlKdVsogg45oMhzEI3odA2g9a5Scw==", + "dependencies": { + "@uploadthing/shared": "^5.0.0" + }, + "peerDependencies": { + "react": "^17.0.2 || ^18.0.0", + "react-dropzone": "^14.2.3", + "uploadthing": "^5.0.0" + } + }, + "node_modules/@uploadthing/shared": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@uploadthing/shared/-/shared-5.0.0.tgz", + "integrity": "sha512-Owr/RbSZgThcjZWWy6xTcmRH6jNcCfTGtrH3jAftA1nlGW1g4FzrdTZIHB2fdjZ5qJJ2KoyY3cPY1wEdE4FjaQ==", + "peerDependencies": { + "@uploadthing/mime-types": "^0.2.0", + "zod": "^3.21.4" + }, + "peerDependenciesMeta": { + "@uploadthing/mime-types": { + "optional": true + } + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1948,6 +1895,14 @@ "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "dev": true }, + "node_modules/attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==", + "engines": { + "node": ">=4" + } + }, "node_modules/autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -2110,18 +2065,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bufferutil": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", - "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -2259,22 +2202,14 @@ } }, "node_modules/class-variance-authority": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.6.0.tgz", - "integrity": "sha512-qdRDgfjx3GRb9fpwpSvn+YaidnT7IUJNe4wt5/SWwM+PmUwJUhQRk/8zAyNro0PmVfmen2635UboTjIBXXxy5A==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.6.1.tgz", + "integrity": "sha512-eurOEGc7YVx3majOrOb099PNKgO3KnKSApOprXI4BTq6bcfbqbQXPN2u+rPPmIJ2di23bMwhk0SxCCthBmszEQ==", "dependencies": { "clsx": "1.2.1" }, "funding": { "url": "https://joebell.co.uk" - }, - "peerDependencies": { - "typescript": ">= 4.5.5 < 6" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, "node_modules/client-only": { @@ -2342,14 +2277,6 @@ "node": ">= 0.6" } }, - "node_modules/cross-fetch": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", - "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", - "dependencies": { - "node-fetch": "^2.6.11" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2381,15 +2308,6 @@ "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", "devOptional": true }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -2558,6 +2476,16 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -2659,39 +2587,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -3185,19 +3080,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3262,6 +3144,17 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-selector": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz", + "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -3698,6 +3591,19 @@ "node": ">=14.18.0" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -4052,11 +3958,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -4511,11 +4412,6 @@ "react-dom": "*" } }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "node_modules/next/node_modules/postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", @@ -4563,16 +4459,6 @@ } } }, - "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, "node_modules/node-releases": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", @@ -5162,7 +5048,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -5220,6 +5105,22 @@ "react": "^18.2.0" } }, + "node_modules/react-dropzone": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz", + "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==", + "dependencies": { + "attr-accept": "^2.2.2", + "file-selector": "^0.6.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, "node_modules/react-hook-form": { "version": "7.45.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.45.0.tgz", @@ -5257,8 +5158,7 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-remove-scroll": { "version": "2.5.5", @@ -5597,6 +5497,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true, + "peer": true + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -6115,11 +6022,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6158,14 +6060,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, "node_modules/typescript": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", @@ -6232,6 +6126,15 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uploadthing": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/uploadthing/-/uploadthing-5.0.0.tgz", + "integrity": "sha512-ahWcBKkcOrEF3NpTH9bpZWS4ZDim92lezd3+uSLkvhUg5kKoGT1+lyOWUUWgiIq9HlYwN8oXIb8LFvg6jLv05Q==", + "dependencies": { + "@uploadthing/mime-types": "^0.2.0", + "@uploadthing/shared": "^5.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -6290,18 +6193,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6332,35 +6223,6 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, - "node_modules/websocket": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", - "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/websocket/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/websocket/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -6443,14 +6305,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", - "engines": { - "node": ">=0.10.32" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index 54fe4a71f79f8db44b4a05968e6f6c0631cc0f79..f75fac391516b01cc693e0d0fce557070af0522b 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "dependencies": { "@auth/prisma-adapter": "^1.0.0", "@hookform/resolvers": "^3.1.1", - "@paralleldrive/cuid2": "^2.2.1", "@prisma/client": "^4.16.1", "@radix-ui/react-aspect-ratio": "^1.0.3", "@radix-ui/react-avatar": "^1.0.3", @@ -27,11 +26,11 @@ "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.4", - "@supabase/supabase-js": "^2.26.0", - "@t3-oss/env-nextjs": "^0.4.1", - "@tanstack/react-query": "^4.29.15", + "@t3-oss/env-nextjs": "^0.6.0", + "@tanstack/react-query": "^4.29.17", + "@uploadthing/react": "^5.0.0", "bcrypt": "^5.1.0", - "class-variance-authority": "^0.6.0", + "class-variance-authority": "^0.6.1", "clsx": "^1.2.1", "dayjs": "^1.11.8", "lucide-react": "^0.252.0", @@ -41,11 +40,13 @@ "normalize-diacritics": "^4.0.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-dropzone": "^14.2.3", "react-hook-form": "^7.45.0", "react-infinite-scroll-component": "^6.1.0", "react-intersection-observer": "^9.5.1", "tailwind-merge": "^1.13.2", "tailwindcss-animate": "^1.0.6", + "uploadthing": "^5.0.0", "zod": "^3.21.4" }, "devDependencies": { @@ -62,4 +63,4 @@ "tailwindcss": "3.3.2", "typescript": "^5.1.3" } -} \ No newline at end of file +} diff --git a/prisma/schema.prisma b/prisma/schema.prisma index bfc5153481d503701f6f2fe3b1ed07f724cbd894..aa9abd8775d257c5569035756c7a059e57ea4e6b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -131,6 +131,7 @@ enum MediaTypes { model Media { id String @id @default(cuid()) url String + key String @unique type MediaTypes @default(IMAGE) gweetId String @map("gweet_id")