diff --git a/.env.example b/.env.example index 673680755844bdbf3adbf3fab1e77a9f73bb9594..31a6eb736513e35d2f52eabc3c3dbc6c3fcdb6c4 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,10 @@ NEXT_PUBLIC_APP_URL="http://localhost:3000" # Database for connecting to Prisma DATABASE_URL="file:./dev.db" +# Database for connecting to Supabase for media +NEXT_PUBLIC_SUPABASE_URL="YOUR_SUPABASE_URL" +NEXT_PUBLIC_SUPABASE_ANON_KEY="YOUR_SUPABASE_ANON_KEY" + # URLs TWITCH_AUTH_BASE_URL="https://id.twitch.tv/oauth2" IGDB_BASE_URL="https://api.igdb.com/v4" diff --git a/app/(content)/(user)/[userid]/status/[id]/page.tsx b/app/(content)/(user)/[userid]/status/[id]/page.tsx index 0bf54b9851eec20fd364ed496a435489de9dbc20..bb63ac57e8ee81ad7aecfd91916b8c8bb0adc888 100644 --- a/app/(content)/(user)/[userid]/status/[id]/page.tsx +++ b/app/(content)/(user)/[userid]/status/[id]/page.tsx @@ -1,7 +1,7 @@ import { GweetDetails } from "@/components/gweets"; import { GweetHeader } from "@/components/layout"; -export default async function TweetClientPage() { +export default async function GweetDetailPage() { return ( <div> <GweetHeader /> diff --git a/app/api/gweets/[id]/route.ts b/app/api/gweets/[id]/route.ts index 067de1e7ba3b5b382ddc47458a409be4553841a3..d75022fa3349d905073cfc39a219774e81899a3a 100644 --- a/app/api/gweets/[id]/route.ts +++ b/app/api/gweets/[id]/route.ts @@ -26,7 +26,7 @@ export async function GET(request: Request, { params }: { params: { id: string } id, }, include: { - user: true, + author: true, likes: { include: { user: { @@ -39,6 +39,42 @@ export async function GET(request: Request, { params }: { params: { id: string } createdAt: "desc", }, }, + media: true, + regweets: { + include: { + user: { + include: { + followers: true, + }, + }, + }, + orderBy: { + createdAt: "desc", + }, + }, + quote: { + include: { + author: true, + media: true, + }, + }, + + allQuotes: { + include: { + likes: true, + regweets: true, + author: true, + quote: { + include: { + author: true, + }, + }, + }, + + orderBy: { + createdAt: "desc", + }, + }, }, }); diff --git a/app/api/gweets/likes/route.ts b/app/api/gweets/likes/route.ts index 0ea6d20a06a0a2253f15f7cd9ee3abbb50ff018f..029782dbf6b4d922157fee03ccca842afd945f5d 100644 --- a/app/api/gweets/likes/route.ts +++ b/app/api/gweets/likes/route.ts @@ -3,7 +3,7 @@ import { z } from "zod"; import { db } from "@/lib/db"; -// get likes +// get likes from user export async function GET(request: Request) { const { searchParams } = new URL(request.url); const user_id = searchParams.get("user_id") || undefined; @@ -31,8 +31,10 @@ export async function GET(request: Request) { }, include: { - user: true, + author: true, + media: true, likes: true, + regweets: true, allComments: true, }, }); @@ -71,12 +73,6 @@ export async function POST(request: Request) { } try { - const gweet = await db.gweet.findUnique({ - where: { - id: gweet_id, - }, - }); - const like = await db.like.findFirst({ where: { gweetId: gweet_id, @@ -91,19 +87,6 @@ export async function POST(request: Request) { }, }); - if (gweet && gweet.likeCount > 0) - await db.gweet.update({ - where: { - id: gweet_id, - }, - - data: { - likeCount: { - decrement: 1, - }, - }, - }); - return NextResponse.json({ message: "Gweet unliked" }); } else { await db.like.create({ @@ -113,20 +96,6 @@ export async function POST(request: Request) { }, }); - if (gweet) { - await db.gweet.update({ - where: { - id: gweet_id, - }, - - data: { - likeCount: { - increment: 1, - }, - }, - }); - } - return NextResponse.json({ message: "Gweet liked" }); } } catch (error: any) { @@ -135,4 +104,4 @@ export async function POST(request: Request) { error: error.message, }); } -} +} \ No newline at end of file diff --git a/app/api/gweets/regweets/route.ts b/app/api/gweets/regweets/route.ts new file mode 100644 index 0000000000000000000000000000000000000000..a7672c140c6fce4b7ce1740495cd6fb8ebffcc84 --- /dev/null +++ b/app/api/gweets/regweets/route.ts @@ -0,0 +1,56 @@ +import { NextResponse } from "next/server"; +import { z } from "zod"; + +import { db } from "@/lib/db"; + +export async function POST(request: Request) { + const { gweet_id, user_id } = await request.json(); + + const regweetSchema = z + .object({ + gweet_id: z.string().cuid(), + user_id: z.string().cuid(), + }) + .strict(); + + const zod = regweetSchema.safeParse({ gweet_id, user_id }); + + if (!zod.success) { + return NextResponse.json( + { + message: "Invalid request body", + error: zod.error.formErrors, + }, { status: 400 }, + ); + } + + try { + const regweet = await db.regweet.findFirst({ + where: { + gweetId: gweet_id, + userId: user_id, + }, + }); + + if (regweet) { + await db.regweet.delete({ + where: { + id: regweet.id, + }, + }); + + return NextResponse.json({ message: "Deleted gweet regweet" }); + } else { + await db.regweet.create({ + data: { + gweetId: gweet_id, + userId: user_id, + }, + }); + + return NextResponse.json({ message: "Gweet regweeted" }); + } + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/app/api/gweets/route.ts b/app/api/gweets/route.ts index 4f208f9c52dcf9e621d495530dc4f45cb178cc8b..60143cc82225996cf8b35937111858166f10f6b0 100644 --- a/app/api/gweets/route.ts +++ b/app/api/gweets/route.ts @@ -35,14 +35,13 @@ export async function GET(request: Request) { }), ...(type === "user_gweets" && { - userId: id, + authorId: id, }), ...(type === "user_replies" && { - userId: id, + authorId: id, NOT: { replyToGweetId: null, - replyToUserId: null, }, }), @@ -56,9 +55,20 @@ export async function GET(request: Request) { }, include: { - user: true, + author: true, likes: true, + media: true, + regweets: true, + + quote: { + include: { + author: true, + media: true, + }, + }, + allComments: true, + allQuotes: true, }, orderBy: { @@ -81,9 +91,9 @@ export async function POST(request: Request) { const gweetSchema = z .object({ content: z.string().min(1).max(280), - userId: z.string().cuid(), - replyToUserId: z.string().optional(), + authorId: z.string().cuid(), replyToGweetId: z.string().cuid().optional(), + quoteGweetId: z.string().cuid().optional(), }) .strict(); diff --git a/components.json b/components.json new file mode 100644 index 0000000000000000000000000000000000000000..819a98d85f53d2dcf9e73a8c74a37738e6e37d9b --- /dev/null +++ b/components.json @@ -0,0 +1,14 @@ +{ + "style": "default", + "rsc": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "app/globals.css", + "baseColor": "slate", + "cssVariables": true + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} \ 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 597630c926ca9095b91a8fda96317efbf8935642..799dc8a223c737404478b18ecce6a664a5794c00 100644 --- a/components/create-gweet/api/post-gweet.ts +++ b/components/create-gweet/api/post-gweet.ts @@ -1,23 +1,24 @@ import { postHashtags, retrieveHashtagsFromGweet } from "@/components/trends"; +import { postMedia } from "./post-media"; export const postGweet = async ({ content, + files, userId, - replyToUserId, replyToGweetId, + quoteGweetId, }: { content: string; + files: File[]; userId: string; - replyToUserId?: string | null; replyToGweetId?: string | null; + quoteGweetId?: string | null; }) => { const gweet = { content, userId, - // if no replyToUserId, don't send it - ...(replyToUserId && { replyToUserId }), - // if no replyToGweetId, don't send it ...(replyToGweetId && { replyToGweetId }), + ...(quoteGweetId && { quoteGweetId }), }; try { @@ -26,6 +27,10 @@ export const postGweet = async ({ body: JSON.stringify(gweet) }).then((result) => result.json()) + if (files.length > 0) { + await postMedia({ files, gweet_id: data.id }); + } + const hashtags = retrieveHashtagsFromGweet(content); if (hashtags) await postHashtags(hashtags); diff --git a/components/create-gweet/api/post-media.ts b/components/create-gweet/api/post-media.ts new file mode 100644 index 0000000000000000000000000000000000000000..7e6676be83d8601a521893f222c146d5d6537841 --- /dev/null +++ b/components/create-gweet/api/post-media.ts @@ -0,0 +1,60 @@ +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 53d13233339b3f9ecae03ad97e2a77707b798571..2c36409e082a5b7793f53b6d0665d510b50ffc77 100644 --- a/components/create-gweet/components/create-gweet-wrapper.tsx +++ b/components/create-gweet/components/create-gweet-wrapper.tsx @@ -5,10 +5,8 @@ import { useState } from "react"; import { CreateGweet } from "./create-gweet"; export const CreateGweetWrapper = ({ - replyToUserId, replyToGweetId, }: { - replyToUserId: string | undefined; replyToGweetId: string | null; }) => { const [isComment, setIsComment] = useState(true); @@ -16,7 +14,6 @@ export const CreateGweetWrapper = ({ return ( <div className="relative border-b border-border"> <CreateGweet - replyToUserId={replyToUserId} replyToGweetId={replyToGweetId} placeholder="Gweet your reply" isComment={isComment} @@ -31,4 +28,4 @@ export const CreateGweetWrapper = ({ )} </div> ); -}; +}; \ No newline at end of file diff --git a/components/create-gweet/components/create-gweet.tsx b/components/create-gweet/components/create-gweet.tsx index 66cf54eb73f0d7dc669959495e3f1409e735d2ec..fc7b35924d283b4a6288f77afabf48e923331969 100644 --- a/components/create-gweet/components/create-gweet.tsx +++ b/components/create-gweet/components/create-gweet.tsx @@ -2,7 +2,8 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useSession } from "next-auth/react"; -import { useState } from "react"; +import Image from "next/image"; +import { useRef, useState } from "react"; import { useForm } from "react-hook-form"; import * as z from "zod"; @@ -25,6 +26,7 @@ import { UserAvatar } from "@/components/user-avatar"; import { Card } from "@/components/ui/card"; import { useCreateGweet } from "../hooks/use-create-gweet"; +import { IChosenImages } from "../types"; const FormSchema = z.object({ gweet: z @@ -35,18 +37,20 @@ const FormSchema = z.object({ export const CreateGweet = ({ parent_gweet, - replyToUserId, + quoted_gweet, replyToGweetId, placeholder, isComment = false, }: { parent_gweet?: IGweet | null; - replyToUserId?: string | null; + quoted_gweet?: IGweet | null; replyToGweetId?: string | null; placeholder?: string | null; isComment?: boolean; }) => { const [isGweetLoading, setIsGweetLoading] = useState<boolean>(false); + const [chosenImages, setChosenImages] = useState<IChosenImages[]>([]); + const imageUploadRef = useRef<HTMLInputElement>(null); const { data: session } = useSession(); const mutation = useCreateGweet(); @@ -62,9 +66,10 @@ export const CreateGweet = ({ mutation.mutate({ content: data.gweet, + files: [], userId: session?.user?.id, - replyToUserId, replyToGweetId, + quoteGweetId: quoted_gweet?.id || null, }) toast({ @@ -75,6 +80,27 @@ export const CreateGweet = ({ form.setValue('gweet', ''); } + const chooseImage = async ( + event: React.ChangeEvent<HTMLInputElement>, + setChosenImages: (images: IChosenImages[]) => void, + ) => { + const file = event?.target?.files?.[0]; + + if (file) { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => { + setChosenImages([ + ...chosenImages, + { + url: reader.result, + file: file, + }, + ]); + }; + } + }; + if (!session) return null; return ( @@ -83,7 +109,7 @@ export const CreateGweet = ({ {parent_gweet && ( <div className="grid place-items-center grid-rows-2 gap-1"> <UserAvatar - user={{ username: parent_gweet?.user?.username, image: parent_gweet?.user?.image || null }} + user={{ username: parent_gweet?.author?.username, image: parent_gweet?.author?.image || null }} /> <div className="bg-gray-300 h-full w-px"></div> </div> @@ -93,10 +119,10 @@ export const CreateGweet = ({ <> <div className="flex gap-1"> <span className="text-secondary text-sm font-medium truncate hover:underline"> - {parent_gweet?.user?.name} + {parent_gweet?.author?.name} </span> <span className="text-tertiary text-sm truncate"> - @{parent_gweet?.user?.email?.split("@")[0]} + @{parent_gweet?.author?.email?.split("@")[0]} </span> <span className="text-tertiary">·</span> </div> @@ -108,11 +134,11 @@ export const CreateGweet = ({ </> )} - {replyToUserId && !isComment && ( + {parent_gweet && !isComment && ( <div className={`${!parent_gweet ? 'ml-16' : ''} flex items-center gap-1 cursor-pointer`}> <span className="text-tertiary truncate">Replying to</span> <span className="text-primary truncate"> - @{replyToUserId} + @{parent_gweet?.authorId} </span> </div> )} @@ -140,7 +166,45 @@ 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. @@ -152,6 +216,13 @@ export const CreateGweet = ({ </FormItem> )} /> + <Button + variant="ghost" + size="sm" + className="absolute bottom-3 right-3" + onClick={() => imageUploadRef.current?.click()} + disabled={isGweetLoading || !session.user} + /> </Card> <div className="flex justify-end"> diff --git a/components/create-gweet/hooks/use-create-gweet.ts b/components/create-gweet/hooks/use-create-gweet.ts index e50883bf0660149f46e87382cf084a8688a126c7..acec8cbe259539694cc8790fa53b2a8a8c00150a 100644 --- a/components/create-gweet/hooks/use-create-gweet.ts +++ b/components/create-gweet/hooks/use-create-gweet.ts @@ -9,20 +9,24 @@ export const useCreateGweet = () => { return useMutation( ({ content, + files, userId, - replyToUserId, replyToGweetId, + quoteGweetId, + }: { content: string; + files: File[]; userId: string; - replyToUserId?: string | null; replyToGweetId?: string | null; + quoteGweetId?: string | null; }) => { return postGweet({ content, + files, userId, - replyToUserId, replyToGweetId, + quoteGweetId, }); }, { diff --git a/components/create-gweet/index.ts b/components/create-gweet/index.ts index 9a73987259895bda3a58279b377d3bd22ddf160f..4d334ccfda7c35e5d02b3fef4c0c15b9d8af4280 100644 --- a/components/create-gweet/index.ts +++ b/components/create-gweet/index.ts @@ -1,3 +1,4 @@ +export * from "./api/post-media"; export * from "./components/create-gweet"; export * from "./components/create-gweet-wrapper"; export * from "./types"; diff --git a/components/create-gweet/types/index.ts b/components/create-gweet/types/index.ts index 1b40498729ecc053da9a58a090ac15d42024e0cb..0e7b1e436493726df1fe7cb78a32864a7b748932 100644 --- a/components/create-gweet/types/index.ts +++ b/components/create-gweet/types/index.ts @@ -1,3 +1,8 @@ export interface Post { id: string; +} + +export interface IChosenImages { + url: string | ArrayBuffer | null; + file: File; } \ No newline at end of file diff --git a/components/following-button.tsx b/components/following-button.tsx deleted file mode 100644 index 420ecdc016ea82d2f78da72783b66dee0ac07c28..0000000000000000000000000000000000000000 --- a/components/following-button.tsx +++ /dev/null @@ -1,55 +0,0 @@ -"use client" - -// import { PrismaClient } from '@prisma/client'; -import { useState } from 'react'; -import { Button } from './ui/button'; - -// Muss in die API route -// const prisma = new PrismaClient(); - -// async function getFollower(userId: number, followerId: number) { -// const follower = await prisma.follows.findFirst({ -// where: { -// followerId: followerId, -// followingId: userId, -// }, -// }); - -// return follower; -// } - -export default function FollowButton({ userId, followerId }: { userId: number; followerId: number }) { - const [isFollowing, setIsFollowing] = useState(false); - - const handleFollow = async () => { - // const follower = await getFollower(userId, followerId); - - // if (follower) { - // // User is already following, so unfollow - // await prisma.follows.delete({ - // where: { - // followerId_followingId: { - // followerId: followerId, - // followingId: userId, - // }, - // }, - // }); - // setIsFollowing(false); - // } else { - // // User is not following, so follow - // await prisma.follows.create({ - // data: { - // followerId: followerId, - // followingId: userId, - // }, - // }); - // setIsFollowing(true); - // } - }; - - return ( - <Button onClick={handleFollow}> - {isFollowing ? 'Unfollow' : 'Follow'} - </Button> - ); -} \ No newline at end of file diff --git a/components/following-users.tsx b/components/following-users.tsx deleted file mode 100644 index 44408c13acfcd0343a27854bd4a9b23de54c74ad..0000000000000000000000000000000000000000 --- a/components/following-users.tsx +++ /dev/null @@ -1,47 +0,0 @@ -"use client" - -// import { PrismaClient } from '@prisma/client'; -import { useEffect, useState } from 'react'; - -// Muss in die API route -// const prisma = new PrismaClient(); - -interface Follower { - id: number; - name: string; - email: string | null; -} - -export default function FollowersList({ userId }: { userId: number }) { - const [followers, setFollowers] = useState<Follower[]>([]); - - useEffect(() => { - async function fetchFollowers() { - // const followersList = await prisma.follows.findMany({ - // where: { - // followingId: userId, - // }, - // include: { - // follower: true, - // }, - // }); - - // const filteredFollowers = followersList.map((follow: any) => { - // const { id, name, email } = follow.follower; - // return { id, name: name ?? "", email }; - // }); - - // setFollowers(filteredFollowers); - } - - fetchFollowers(); - }, [userId]); - - return ( - <ul> - {followers.map((follower) => ( - <li key={follower.id}>{follower.name} ({follower.email})</li> - ))} - </ul> - ); -} \ No newline at end of file diff --git a/components/gweets/api/delete-media.ts b/components/gweets/api/delete-media.ts new file mode 100644 index 0000000000000000000000000000000000000000..20f2d3a7e50832771d878a49593523f6b820ff76 --- /dev/null +++ b/components/gweets/api/delete-media.ts @@ -0,0 +1,29 @@ +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/api/handle-regweet.ts b/components/gweets/api/handle-regweet.ts new file mode 100644 index 0000000000000000000000000000000000000000..0607ca83de69f0c490f915505af599c5cd1d9946 --- /dev/null +++ b/components/gweets/api/handle-regweet.ts @@ -0,0 +1,15 @@ +export const handleRegweet = async (gweetId: string, userId: string) => { + try { + const data = await fetch("/api/gweets/regweets", { + method: "POST", + body: JSON.stringify({ + gweet_id: gweetId, + user_id: userId, + }), + }).then((result) => result.json()); + + return data; + } catch (error: any) { + return error.response.data; + } +}; \ No newline at end of file diff --git a/components/gweets/components/actions/like-button.tsx b/components/gweets/components/actions/like-button.tsx index cdfa89e1e9ed2925b262967bc26242209634debc..2c0f192acaafc47c764a5c7fb697bb4fb5e3f0ec 100644 --- a/components/gweets/components/actions/like-button.tsx +++ b/components/gweets/components/actions/like-button.tsx @@ -19,7 +19,7 @@ export const LikeButton = ({ ); const mutation = useLike({ - gweetAuthorId: gweet?.user?.id, + gweetAuthorId: gweet?.author?.id, sessionOwnerId: session?.user?.id, }); diff --git a/components/gweets/components/actions/regweet-button.tsx b/components/gweets/components/actions/regweet-button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d1abfd461cb48d46b6b7070a990e4c00ffe89c22 --- /dev/null +++ b/components/gweets/components/actions/regweet-button.tsx @@ -0,0 +1,41 @@ +import { Icons } from "@/components/icons"; +import { Button } from "@/components/ui/button"; +import { useSession } from "next-auth/react"; +import { useRegweet } from "../../hooks/use-regweet"; +import { IGweet } from "../../types"; + +export const RegweetButton = ({ gweet }: { gweet: IGweet }) => { + const { data: session } = useSession(); + const hasRegweeted = gweet?.regweets?.some( + (regweet) => regweet?.userId === session?.user?.id, + ); + + const mutation = useRegweet(); + + return ( + <Button + onClick={(e) => { + e.stopPropagation(); + if (!session) { + return; // TODO: show login modal + } + mutation.mutate({ + gweetId: gweet?.id, + userId: session?.user?.id, + }); + }} + 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" /> + } + + <span className="pl-2"> + {gweet && gweet?.regweets?.length > 0 && ( + <span className="">{gweet?.regweets?.length}</span> + )} + </span> + </Button> + ); +}; \ No newline at end of file diff --git a/components/gweets/components/comments.tsx b/components/gweets/components/comments.tsx index 5841d8bc4d1ff9eefa877a6d8838ab6465a1fd19..8cbc59d40ba8c1bc6ce126a83805f044b8567cbc 100644 --- a/components/gweets/components/comments.tsx +++ b/components/gweets/components/comments.tsx @@ -21,7 +21,7 @@ export const Comments = ({ gweetId }: { gweetId: string }) => { }); if (isLoading) { - return <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />; + return <Icons.spinner className="mt-3 h-4 w-4 animate-spin align-middle" />; } if (isError) { diff --git a/components/gweets/components/delete-gweet-modal.tsx b/components/gweets/components/delete-gweet-modal.tsx index 5cc6fdfabd592d95d3b434b42e4be186bd34efce..6488b66d86391929a1af276939e9b9cdd4fd2ff0 100644 --- a/components/gweets/components/delete-gweet-modal.tsx +++ b/components/gweets/components/delete-gweet-modal.tsx @@ -1,3 +1,6 @@ +"use client"; + +import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { useDeleteGweet } from "../hooks/use-delete-gweet"; import { IGweet } from "../types"; @@ -12,25 +15,29 @@ import { DialogTrigger, } from "@/components/ui/dialog"; -export const DeleteGweetModal = ({ - gweet, -}: { - gweet: IGweet; -}) => { +export const DeleteGweetModal = ({ gweet, props, forwardedRef }: { gweet: IGweet, props: any, forwardedRef: any }) => { + const { triggerChildren, onSelect, onOpenChange, ...itemProps } = props; const mutation = useDeleteGweet(); return ( - <Dialog> + <Dialog onOpenChange={onOpenChange}> <DialogTrigger asChild> - <Button variant="outline">Delete gweet?</Button> + <DropdownMenuItem + {...itemProps} + ref={forwardedRef} + onSelect={(event) => { + event.preventDefault(); + onSelect && onSelect(); + }}> + {triggerChildren} + </DropdownMenuItem> </DialogTrigger> <DialogContent className="sm:max-w-[425px]"> <DialogHeader> <DialogTitle>Delete gweet?</DialogTitle> <DialogDescription> - This can't be undone and it will be removed from your profile, the - timeline of any accounts that follow you, and from Twitter search - results. + You are about to delete this gweet. This can't be undone and will be removed from your profile, + the timeline of any accounts that follow you, and from the search results. </DialogDescription> </DialogHeader> <DialogFooter> @@ -44,4 +51,4 @@ export const DeleteGweetModal = ({ </DialogContent> </Dialog> ) -} +} \ No newline at end of file diff --git a/components/gweets/components/gweet-actions.tsx b/components/gweets/components/gweet-actions.tsx index b57c5d9aca4d3447d2be03e0e97b7734d47720bc..e8589cdaf3f3dc56555041cb22fdee98f33a4bea 100644 --- a/components/gweets/components/gweet-actions.tsx +++ b/components/gweets/components/gweet-actions.tsx @@ -2,19 +2,20 @@ import { IGweet } from "../types"; import { CommentButton } from "./actions/comment-button"; import { LikeButton } from "./actions/like-button"; +import { RegweetButton } from "./actions/regweet-button"; export const GweetActions = ({ gweet, - showStats = false, }: { gweet: IGweet; showStats?: boolean | undefined; }) => { return ( - <div className=" space-x-2"> + <div className="space-x-2"> <CommentButton gweet={gweet} showStats={showStats} /> + <RegweetButton gweet={gweet} /> <LikeButton gweet={gweet} smallIcons={false} showStats={showStats} /> </div> ); -}; +}; \ No newline at end of file diff --git a/components/gweets/components/gweet-author.tsx b/components/gweets/components/gweet-author.tsx new file mode 100644 index 0000000000000000000000000000000000000000..fb00c45aa5fa6498988a03db624a93728ea420a3 --- /dev/null +++ b/components/gweets/components/gweet-author.tsx @@ -0,0 +1,22 @@ +import { GweetOptions, IGweet } from "@/components/gweets"; +import { UserAvatar } from "@/components/user-avatar"; + +export const GweetAuthor = ({ gweet }: { gweet: IGweet }) => { + return ( + <div className="flex items-center"> + <UserAvatar + user={{ username: gweet.author.username, image: gweet.author.image }} + className="h-10 w-10" + /> + + <div> + <span className="font-bold mr-2">{gweet.author.name}</span> + <span className="text-sky-500 text-sm">@{gweet.author.username}</span> + </div> + + <div className="ml-auto"> + <GweetOptions gweet={gweet} /> + </div> + </div> + ); +}; \ No newline at end of file diff --git a/components/gweets/components/gweet-creation-date.tsx b/components/gweets/components/gweet-creation-date.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9c5919733d0491b49c04a2fd8c7f94b14b137b9b --- /dev/null +++ b/components/gweets/components/gweet-creation-date.tsx @@ -0,0 +1,11 @@ +import dayjs from "dayjs"; + +export const GweetCreationDate = ({ date }: { date: Date }) => { + return ( + <div> + <span>{dayjs(date).format(`h:mm A`)}</span> + <span>·</span> + <span>{dayjs(date).format(`MMM D, YYYY`)}</span> + </div> + ); +}; \ No newline at end of file diff --git a/components/gweets/components/gweet-details.tsx b/components/gweets/components/gweet-details.tsx index d3d998deb4ceb1b3cf91be4f24a392d63d198817..53d75fc2bfab9942ef6dc8e96d42e75210a5ee14 100644 --- a/components/gweets/components/gweet-details.tsx +++ b/components/gweets/components/gweet-details.tsx @@ -1,21 +1,19 @@ "use client" -import { usePathname, useRouter } from "next/navigation"; - import { CreateGweetWrapper } from "@/components/create-gweet"; -import { TryAgain } from "@/components/try-again"; - -import { useGweet } from "../hooks/use-gweet"; - import { Icons } from "@/components/icons"; +import { TryAgain } from "@/components/try-again"; import { Card } from "@/components/ui/card"; -import { UserAvatar } from "@/components/user-avatar"; -import { formatTimeElapsed } from "@/lib/utils"; +import Image from "next/image"; +import { usePathname } from "next/navigation"; +import { useGweet } from "../hooks/use-gweet"; import { Comments } from "./comments"; import { GweetActions } from "./gweet-actions"; +import { GweetAuthor } from "./gweet-author"; +import { GweetCreationDate } from "./gweet-creation-date"; export const GweetDetails = () => { - const router = useRouter(); + // TODO use params const pathname = usePathname(); const id = pathname?.split(`/`)[3] || ``; @@ -33,54 +31,49 @@ export const GweetDetails = () => { return <>Not Found!</> } - console.log(`gweet`, gweet); - return ( <Card className="w-full h-full mt-12 p-2 xl:p-4 "> - <div className="flex"> - {/* make gweet-author like twitter */} - <UserAvatar - user={{ username: gweet.user.username, image: gweet.user.image }} - className="h-10 w-10" - /> - <div className="flex items-center"> - <h1 className="font-bold mr-2">{gweet.user.name}</h1> - <h1 className="text-sky-500 text-sm"> - @{gweet.user.username} - </h1> - </div> + <div className="flex flex-col space-y-2"> + <GweetAuthor gweet={gweet} /> - {/* {gweet?.replyToGweetId && ( - <div className=""> - <span className="">Replying to</span> - <button - onClick={(e) => { - e.stopPropagation(); - router.push(`/${gweet?.replyToUserId}`); - }} - className="" - > - @{gweet?.replyToUserId} - </button> - </div> - )} */} + {/* TODO needs handling of all gweets above and under the gweet */} - {gweet?.content && <h1>{gweet?.content}</h1>} + <div> + {gweet.content && <h1>{gweet.content}</h1>} - {/* make gweet creation date like twitter */} - {formatTimeElapsed(gweet.createdAt)} - <div className=""> - <GweetActions gweet={gweet} /> + {gweet.media && gweet.media.length > 0 && ( + <div className={`grid object-cover grid-cols- + ${gweet.media.length === 1 ? "1" + : gweet.media.length === 2 ? "2 space-x-3" + : gweet.media.length === 3 ? "3 space-x-3 space-y-3" + : gweet.media.length === 4 ? "4 space-x-3 space-y-3" + : "" + }`} + > + {gweet.media.slice(0, 4).map((media) => { + return ( + <Image + key={media.id} + src={media.url} + alt={"image-" + media.id} + width={1000} + height={1000} + /> + ); + })} + </div> + )} + + {/* TODO */} + {/* {gweet?.quotedGweet && <QuotedGweet gweet={gweet?.quotedGweet} />} */} </div> - </div> - <CreateGweetWrapper - replyToUserId={gweet?.user?.email?.split(`@`)[0]} - replyToGweetId={gweet?.id} - /> - <div className=""> - <Comments gweetId={gweet?.id} /> + <GweetCreationDate date={gweet.createdAt} /> + <GweetActions gweet={gweet} /> </div> + + <CreateGweetWrapper replyToGweetId={gweet?.id} /> + <Comments gweetId={gweet?.id} /> </Card> ); }; \ No newline at end of file diff --git a/components/gweets/components/gweet-options.tsx b/components/gweets/components/gweet-options.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c975c341a032cead257e8f475250fd7962a62fca --- /dev/null +++ b/components/gweets/components/gweet-options.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { IGweet } from "@/components/gweets"; +import { Icons } from "@/components/icons"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { toast } from "@/components/ui/use-toast"; +import { env } from "@/env.mjs"; +import { useSession } from "next-auth/react"; +import { useRef, useState } from "react"; +import { DeleteGweetModal } from "./delete-gweet-modal"; + +export const GweetOptions = ({ gweet }: { gweet: IGweet }) => { + const { data: session } = useSession(); + + const [dropdownOpen, setDropdownOpen] = useState<boolean>(false); + const [hasOpenDialog, setHasOpenDialog] = useState<boolean>(false); + const dropdownTriggerRef = useRef<HTMLButtonElement | null>(null); + const focusRef = useRef<HTMLButtonElement | null>(null); + + function handleDialogItemSelect() { + focusRef.current = dropdownTriggerRef.current; + } + + function handleDialogItemOpenChange(open: boolean) { + setHasOpenDialog(open); + if (open === false) { + setDropdownOpen(false); + } + } + + const url = `${env.NEXT_PUBLIC_APP_URL}/status/${gweet?.id}`; + + return ( + <DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}> + <DropdownMenuTrigger asChild> + <Button variant="ghost" size="icon" ref={dropdownTriggerRef}> + <Icons.moreOptions /> + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent + className="w-56 font-bold cursor-pointer" + hidden={hasOpenDialog} + onCloseAutoFocus={(event) => { + if (focusRef.current) { + focusRef.current.focus(); + focusRef.current = null; + event.preventDefault(); + } + }}> + <DropdownMenuGroup> + {gweet.author.id === session?.user?.id && ( + <DeleteGweetModal + gweet={gweet} + props={{ + triggerChildren: ( + <div className="text-red-600"> + <Icons.trash className="mr-2 h-4 w-4 " /> + <span>Delete</span> + </div> + ), + onSelect: handleDialogItemSelect, + onOpenChange: handleDialogItemOpenChange, + }} + forwardedRef={focusRef} + /> + )} + <DropdownMenuItem + onClick={() => { + navigator.clipboard.writeText(url); + return toast({ + description: "Copied link to gweet.", + }) + }}> + <Icons.link className="mr-2 h-4 w-4 " /> + <span>Copy link to gweet</span> + </DropdownMenuItem> + </DropdownMenuGroup> + </DropdownMenuContent> + </DropdownMenu> + ); +}; \ No newline at end of file diff --git a/components/gweets/components/gweet.tsx b/components/gweets/components/gweet.tsx index e4aad6ae9fd200815c1c7203876021fd15f49ee1..6c538da72919f6cdfab3742b272f09b538d6dd7f 100644 --- a/components/gweets/components/gweet.tsx +++ b/components/gweets/components/gweet.tsx @@ -13,19 +13,19 @@ export const Gweet = ({ gweet }: { gweet: IGweet }) => { <Button variant="ghost" tabIndex={0} - onClick={() => router.push(`/${gweet.user.username}/status/${gweet.id}`)} + onClick={() => router.push(`/${gweet.author.username}/status/${gweet.id}`)} className="flex h-auto w-full text-left" > <UserAvatar - user={{ username: gweet.user.username, image: gweet.user.image }} + user={{ username: gweet.author.username, image: gweet.author.image }} className="h-10 w-10" /> <div className="ml-4 flex flex-col flex-grow"> <div> <div className="flex items-center"> - <h1 className="font-bold mr-2">{gweet.user.name}</h1> + <h1 className="font-bold mr-2">{gweet.author.name}</h1> <h1 className="text-sky-500 text-sm"> - @{gweet.user.username} + @{gweet.author.username} </h1> <h1 className="text-gray-500 text-sm ml-auto"> {formatTimeElapsed(gweet.createdAt)} @@ -39,11 +39,11 @@ export const Gweet = ({ gweet }: { gweet: IGweet }) => { <button onClick={(e) => { e.stopPropagation(); - router.push(`/${gweet?.replyToUserId}`); + router.push(`/${gweet?.author.username}`); }} className="" > - @{gweet?.replyToUserId} + @{gweet?.author.username} </button> </div> )} diff --git a/components/gweets/components/infinite-gweets.tsx b/components/gweets/components/infinite-gweets.tsx index b96c79e7eb7fc354117939c3c9a8ccd45d297ea8..e923d878fe73dc7f00ca724dbaf2429476748cef 100644 --- a/components/gweets/components/infinite-gweets.tsx +++ b/components/gweets/components/infinite-gweets.tsx @@ -35,7 +35,6 @@ export const InfiniteGweets = ({ {isSuccess && gweets?.pages?.map((page) => { return page?.gweets?.map((gweet, index) => ( - console.log(gweet.userId), <div ref={index === page.gweets.length - 4 ? ref : undefined} key={gweet.id} diff --git a/components/gweets/hooks/use-regweet.ts b/components/gweets/hooks/use-regweet.ts new file mode 100644 index 0000000000000000000000000000000000000000..f46cb4dc7724e26150ed67b84476b3f506f244fb --- /dev/null +++ b/components/gweets/hooks/use-regweet.ts @@ -0,0 +1,26 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +import { handleRegweet } from "../api/handle-regweet"; + +export const useRegweet = () => { + const QueryClient = useQueryClient(); + + return useMutation( + ({ gweetId, userId }: { gweetId: string; userId: string }) => { + return handleRegweet(gweetId, userId); + }, + + { + onSuccess: () => { + QueryClient.invalidateQueries(["gweets"]); + QueryClient.invalidateQueries(["users"]); + }, + + onError: (error: any) => { + console.log(error); + }, + + onSettled: () => { }, + }, + ); +}; \ No newline at end of file diff --git a/components/gweets/index.ts b/components/gweets/index.ts index 412a93cc19dc6af721be99cb39f6a08097ba2e35..fb59a417b41449a937b135ee69004a2cfacc86a6 100644 --- a/components/gweets/index.ts +++ b/components/gweets/index.ts @@ -1,6 +1,10 @@ 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 a385f50571f27a96b8447180955b3989a8f7a980..ca430e7325b7776d6a404449c63296ff65a6a46d 100644 --- a/components/gweets/types/index.ts +++ b/components/gweets/types/index.ts @@ -1,4 +1,4 @@ -import { Gweet, Like } from "@prisma/client"; +import { Gweet, Like, Media, Regweet } from "@prisma/client"; import { IUser } from "@/components/profile"; @@ -7,8 +7,12 @@ export interface IFeed { } export interface IGweet extends Gweet { - user: IUser; + author: IUser; + quotedGweet: IGweet; likes: ILike[]; + media: IMedia[]; + regweets: IRegweet[]; + quotes: IGweet[]; comments: IGweet[]; } @@ -17,6 +21,14 @@ export interface ILike extends Like { gweet: IGweet; } +export interface IMedia extends Media { + gweet: IGweet; +} + +export interface IRegweet extends Regweet { + user: IUser; +} + export interface IInfiniteGweets { pages: { gweets: IGweet[]; nextId?: string | undefined }[]; pageParams: any; diff --git a/components/icons.tsx b/components/icons.tsx index cdacdf511e61cfebad9cebf775031ff81d2908ab..35f194bd986048c9d630a78c0890321c6616d137 100644 --- a/components/icons.tsx +++ b/components/icons.tsx @@ -17,13 +17,16 @@ import { Home, Image, Laptop, + Link, Loader2, LucideProps, MessageCircle, Moon, + MoreHorizontal, MoreVertical, Pizza, Plus, + Repeat2, Settings, SunMedium, Trash, @@ -78,9 +81,12 @@ export const Icons: IconsType = { chevronLeft: ChevronLeft, // Back Login Arrow spinner: Loader2, // Loading Spinner github: Github, // Github Icon - close: X, - chevronRight: ChevronRight, - trash: Trash, + close: X, // Close Button + moreOptions: MoreHorizontal, // More Options Button + chevronRight: ChevronRight, // dropdown chevron + trash: Trash, // Delete Button + link: Link, // Link Button + regweet: Repeat2, // Regweet Button post: FileText, page: File, media: Image, diff --git a/components/ui/button.tsx b/components/ui/button.tsx index 7419a7f5404578220055d1aaa678470e77c72349..c89803273006651945849f7f31898e88add3c5d7 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -23,6 +23,7 @@ const buttonVariants = cva( default: "h-10 py-2 px-4", sm: "h-9 px-3 rounded-md", lg: "h-11 px-8 rounded-full", + icon: "h-10 w-10", }, }, defaultVariants: { diff --git a/components/ui/sheet.tsx b/components/ui/sheet.tsx new file mode 100644 index 0000000000000000000000000000000000000000..98cdd40d2a04c51a5fb80501bdd359eb6f3bc91d --- /dev/null +++ b/components/ui/sheet.tsx @@ -0,0 +1,144 @@ +"use client" + +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Sheet = SheetPrimitive.Root + +const SheetTrigger = SheetPrimitive.Trigger + +const SheetClose = SheetPrimitive.Close + +const SheetPortal = ({ + className, + ...props +}: SheetPrimitive.DialogPortalProps) => ( + <SheetPrimitive.Portal className={cn(className)} {...props} /> +) +SheetPortal.displayName = SheetPrimitive.Portal.displayName + +const SheetOverlay = React.forwardRef< + React.ElementRef<typeof SheetPrimitive.Overlay>, + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay> +>(({ className, ...props }, ref) => ( + <SheetPrimitive.Overlay + className={cn( + "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", + className + )} + {...props} + ref={ref} + /> +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, + VariantProps<typeof sheetVariants> {} + +const SheetContent = React.forwardRef< + React.ElementRef<typeof SheetPrimitive.Content>, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + <SheetPortal> + <SheetOverlay /> + <SheetPrimitive.Content + ref={ref} + className={cn(sheetVariants({ side }), className)} + {...props} + > + {children} + <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> + <X className="h-4 w-4" /> + <span className="sr-only">Close</span> + </SheetPrimitive.Close> + </SheetPrimitive.Content> + </SheetPortal> +)) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes<HTMLDivElement>) => ( + <div + className={cn( + "flex flex-col space-y-2 text-center sm:text-left", + className + )} + {...props} + /> +) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes<HTMLDivElement>) => ( + <div + className={cn( + "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", + className + )} + {...props} + /> +) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef<typeof SheetPrimitive.Title>, + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title> +>(({ className, ...props }, ref) => ( + <SheetPrimitive.Title + ref={ref} + className={cn("text-lg font-semibold text-foreground", className)} + {...props} + /> +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef<typeof SheetPrimitive.Description>, + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description> +>(({ className, ...props }, ref) => ( + <SheetPrimitive.Description + ref={ref} + className={cn("text-sm text-muted-foreground", className)} + {...props} + /> +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} diff --git a/components/ui/textarea.tsx b/components/ui/textarea.tsx index 8e7672b8ae0f8329934ac91b351457afd14f9ca4..dc548f3150601861c5d4b78428cb939abd06bc4c 100644 --- a/components/ui/textarea.tsx +++ b/components/ui/textarea.tsx @@ -22,4 +22,3 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( Textarea.displayName = "Textarea" export { Textarea } - diff --git a/components/ui/toast.tsx b/components/ui/toast.tsx index f8205889e631bae2ddc6941056bc5f39c13ccb8b..32ffed0b1cbe08afdef2b2c8f7923590c7275db4 100644 --- a/components/ui/toast.tsx +++ b/components/ui/toast.tsx @@ -1,7 +1,7 @@ -import * as React from "react" import * as ToastPrimitives from "@radix-ui/react-toast" import { cva, type VariantProps } from "class-variance-authority" import { X } from "lucide-react" +import * as React from "react" import { cn } from "@/lib/utils" @@ -41,7 +41,7 @@ const toastVariants = cva( const Toast = React.forwardRef< React.ElementRef<typeof ToastPrimitives.Root>, React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & - VariantProps<typeof toastVariants> + VariantProps<typeof toastVariants> >(({ className, variant, ...props }, ref) => { return ( <ToastPrimitives.Root @@ -115,13 +115,5 @@ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast> type ToastActionElement = React.ReactElement<typeof ToastAction> export { - type ToastProps, - type ToastActionElement, - ToastProvider, - ToastViewport, - Toast, - ToastTitle, - ToastDescription, - ToastClose, - ToastAction, + Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, type ToastActionElement, type ToastProps } diff --git a/components/ui/toaster.tsx b/components/ui/toaster.tsx index ac9370c3ca8b43935a190b0cc2df6fb1823a570a..4414b1cbbb7e44feafe6fb205bb61c9278524a37 100644 --- a/components/ui/toaster.tsx +++ b/components/ui/toaster.tsx @@ -1,12 +1,12 @@ "use client" import { - Toast, - ToastClose, - ToastDescription, - ToastProvider, - ToastTitle, - ToastViewport, + Toast, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, } from "@/components/ui/toast" import { useToast } from "@/components/ui/use-toast" diff --git a/components/user-item.tsx b/components/user-item.tsx deleted file mode 100644 index 64c729dfc82eeec37114594519bd6117190d9081..0000000000000000000000000000000000000000 --- a/components/user-item.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import Image from "next/image"; -import Link from "next/link"; -import FollowButton from "./following-button"; - -// this is a single user helper-component, only for design purposes -export default function FollowUser({ id, followId, username, image }: { id: number, followId: number, username: string, image: { url: string } }) { - return ( - <div> - <Link href={`/user/${id}`}> - <div className=""> - <Image - src={image.url} - alt={username} - width={50} - height={50} - priority={true} /> - </div> - <p>{username}</p> - <FollowButton userId={id} followerId={followId} /> - </Link> - </div> - ) -} \ No newline at end of file diff --git a/env.mjs b/env.mjs index 0154d5d3c4256505541cf0c7bb370f29cdf98395..2e2b1ee7d6497494bc6d5e17907b273e20825892 100644 --- a/env.mjs +++ b/env.mjs @@ -16,6 +16,8 @@ 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, @@ -24,6 +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, 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 new file mode 100644 index 0000000000000000000000000000000000000000..d08c18ca1f9e18d2a508f3f60da0a12d9cc1de28 --- /dev/null +++ b/lib/db-media.ts @@ -0,0 +1,9 @@ +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/package-lock.json b/package-lock.json index 9a4e70b61cf4bf6a94b49fec09edc894ad04f725..84a56ec586d99bd4342316dd36cd08185b68246a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "@auth/prisma-adapter": "^1.0.0", "@hookform/resolvers": "^3.1.1", - "@prisma/client": "^4.16.0", + "@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", "@radix-ui/react-dialog": "^1.0.4", @@ -22,12 +23,14 @@ "@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", "bcrypt": "^5.1.0", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", - "lucide-react": "^0.248.0", + "dayjs": "^1.11.8", + "lucide-react": "^0.252.0", "next": "^13.4.7", "next-auth": "^4.22.1", "next-themes": "^0.2.1", @@ -45,13 +48,13 @@ "@tanstack/eslint-plugin-query": "^4.29.9", "@types/bcrypt": "^5.0.0", "@types/node": "^20.3.1", - "@types/react": "^18.2.13", + "@types/react": "^18.2.14", "@types/react-dom": "^18.2.6", "autoprefixer": "10.4.14", "eslint": "^8.43.0", "eslint-config-next": "^13.4.7", "postcss": "8.4.24", - "prisma": "^4.16.0", + "prisma": "^4.16.1", "tailwindcss": "3.3.2", "typescript": "^5.1.3" } @@ -88,26 +91,6 @@ } } }, - "node_modules/@auth/core/node_modules/preact": { - "version": "10.11.3", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", - "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, - "node_modules/@auth/core/node_modules/preact-render-to-string": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz", - "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==", - "dependencies": { - "pretty-format": "^3.8.0" - }, - "peerDependencies": { - "preact": ">=10" - } - }, "node_modules/@auth/prisma-adapter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@auth/prisma-adapter/-/prisma-adapter-1.0.0.tgz", @@ -120,9 +103,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", + "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", "dependencies": { "regenerator-runtime": "^0.13.11" }, @@ -187,24 +170,24 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.6.tgz", - "integrity": "sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz", + "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==" }, "node_modules/@floating-ui/dom": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.8.tgz", - "integrity": "sha512-XLwhYV90MxiHDq6S0rzFZj00fnDM+A1R9jhSioZoMsa7G0Q0i+Q4x40ajR8FHSdYDE1bgjG45mIWe6jtv9UPmg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.2.tgz", + "integrity": "sha512-VKmvHVatWnewmGGy+7Mdy4cTJX71Pli6v/Wjb5RQBuq5wjUYx+Ef+kRThi8qggZqDgD8CogCpqhRoVp3+yQk+g==", "dependencies": { - "@floating-ui/core": "^1.2.6" + "@floating-ui/core": "^1.3.1" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.0.tgz", - "integrity": "sha512-Ke0oU3SeuABC2C4OFu2mSAwHIP5WUiV98O9YWoHV4Q5aT6E9k06DV0Khi5uYspR8xmmBk08t8ZDcz3TR3ARkEg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz", + "integrity": "sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==", "dependencies": { - "@floating-ui/dom": "^1.2.7" + "@floating-ui/dom": "^1.3.0" }, "peerDependencies": { "react": ">=16.8.0", @@ -468,6 +451,17 @@ "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", @@ -508,10 +502,18 @@ "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.0", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.0.tgz", - "integrity": "sha512-2OCURAmRtdlL8iUDTypMrrxfwe8frXTeXaxGsVOaYtc/wrUyk8Z/0OBetM7cdlsy7ZFWlMX72VogKeh+A4Xcjw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", @@ -529,9 +531,9 @@ } }, "node_modules/@prisma/client": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.16.0.tgz", - "integrity": "sha512-CBD+5IdZPiavhLkQokvsz1uz4r9ppixaqY/ajybWs4WXNnsDVMBKEqN3BiPzpSo79jiy22VKj/67pqt4VwIg9w==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.16.1.tgz", + "integrity": "sha512-CoDHu7Bt+NuDo40ijoeHP79EHtECsPBTy3yte5Yo3op8TqXt/kV0OT5OrsWewKvQGKFMHhYQ+ePed3zzjYdGAw==", "hasInstallScript": true, "dependencies": { "@prisma/engines-version": "4.16.0-66.b20ead4d3ab9e78ac112966e242ded703f4a052c" @@ -549,9 +551,9 @@ } }, "node_modules/@prisma/engines": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.16.0.tgz", - "integrity": "sha512-M6XoMRXnqL0rqZGQS8ZpNiHYG4G1fKBdoqW/oBtHnr1in5UYgerZqal3CXchmd6OBD/770PE9dtjQuqcilZJUA==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.16.1.tgz", + "integrity": "sha512-gpZG0kGGxfemgvK/LghHdBIz+crHkZjzszja94xp4oytpsXrgt/Ice82MvPsWMleVIniKuARrowtsIsim0PFJQ==", "devOptional": true, "hasInstallScript": true }, @@ -1408,11 +1410,66 @@ } }, "node_modules/@rushstack/eslint-patch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz", - "integrity": "sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz", + "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", @@ -1453,20 +1510,21 @@ } }, "node_modules/@tanstack/query-core": { - "version": "4.29.15", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.29.15.tgz", - "integrity": "sha512-Recc1d5rjHesKhzlH3Aw66v+vQxtB9OHEXP/vxgEcEJ0DwEpfe3EQ4id20vuBJHY2XRjfgWGmUs6ZgK6PSsTXA==", + "version": "4.29.17", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.29.17.tgz", + "integrity": "sha512-iDbO8yZOpm1lqgq6L8mpxGbKaoiyZSjthxEB3WGU7mNPYss9q4H3Q67+e2xXGwkemEVmtEX/WwvtFitrvVU8TA==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/react-query": { - "version": "4.29.15", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.29.15.tgz", - "integrity": "sha512-1zDkv95ljuJ623hhbYU8YIprPW2x6774kh3IQNEuZav62+S+Zr26uUOrE2zGRp9I1uO5Liw/0uYB3dWXQP5+3Q==", + "version": "4.29.17", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.29.17.tgz", + "integrity": "sha512-udOy/jgqiBHBAP93YPAU3QoVYO+Rtx9HT/10xGDQzC8iQU/wIxcIaT/usX+1NSzoUFYU5hUcPaNErPWZnR7XgA==", "dependencies": { - "@tanstack/query-core": "4.29.15", + "@tanstack/query-core": "4.29.17", + "client-only": "0.0.1", "use-sync-external-store": "^1.2.0" }, "funding": { @@ -1505,8 +1563,12 @@ "node_modules/@types/node": { "version": "20.3.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", - "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==", - "dev": true + "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==" }, "node_modules/@types/prop-types": { "version": "15.7.5", @@ -1515,9 +1577,9 @@ "devOptional": true }, "node_modules/@types/react": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.13.tgz", - "integrity": "sha512-vJ+zElvi/Zn9cVXB5slX2xL8PZodPCwPRDpittQdw43JR2AJ5k3vKdgJJyneV/cYgIbLQUwXa9JVDvUZXGba+Q==", + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz", + "integrity": "sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==", "devOptional": true, "dependencies": { "@types/prop-types": "*", @@ -1540,15 +1602,23 @@ "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.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.7.tgz", - "integrity": "sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", + "integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/typescript-estree": "5.59.7", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/typescript-estree": "5.60.0", "debug": "^4.3.4" }, "engines": { @@ -1568,13 +1638,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz", - "integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", + "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7" + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1585,9 +1655,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz", - "integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", + "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1598,13 +1668,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz", - "integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", + "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1625,12 +1695,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", - "integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", + "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/types": "5.60.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1647,9 +1717,9 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1774,12 +1844,12 @@ } }, "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "dependencies": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "node_modules/array-buffer-byte-length": { @@ -1924,21 +1994,21 @@ } }, "node_modules/axe-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.1.tgz", - "integrity": "sha512-sCXXUhA+cljomZ3ZAwb8i1p3oOlkABzPy08ZDAoGcYuvtBPlQ1Ytde129ArXyHWDhfeewq7rlx9F+cUx2SSlkg==", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz", + "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/axobject-query": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", - "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", "dev": true, "dependencies": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "node_modules/balanced-match": { @@ -2009,9 +2079,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -2021,13 +2091,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -2036,6 +2110,18 @@ "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", @@ -2093,9 +2179,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001489", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz", - "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==", + "version": "1.0.30001508", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", + "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", "funding": [ { "type": "opencollective", @@ -2256,6 +2342,14 @@ "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", @@ -2287,12 +2381,26 @@ "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", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/dayjs": { + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", + "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2309,35 +2417,6 @@ } } }, - "node_modules/deep-equal": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz", - "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.0", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2411,6 +2490,15 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", @@ -2459,9 +2547,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.404", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.404.tgz", - "integrity": "sha512-te57sWvQdpxmyd1GiswaodKdXdPgn9cN4ht8JlNa04QgtrfnUdWEo1261rY2vaC6TKaiHn0E7QerJWPKFCvMVw==", + "version": "1.4.440", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz", + "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==", "dev": true }, "node_modules/emoji-regex": { @@ -2471,9 +2559,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.0.tgz", - "integrity": "sha512-+DCows0XNwLDcUhbFJPdlQEVnT2zXlCv7hPxemTz86/O+B/hCQ+mb7ydkPKiflpVraqLPCAfu7lDy+hBXueojw==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -2531,26 +2619,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-set-tostringtag": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", @@ -2591,6 +2659,39 @@ "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", @@ -2741,9 +2842,9 @@ } }, "node_modules/eslint-import-resolver-typescript/node_modules/globby": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", - "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.0.tgz", + "integrity": "sha512-jWsQfayf13NvqKUIL3Ta+CIqMnvlaIDFveWE/dpOZ9+3AMEJozsxDvKA02zync9UuvOM8rOXzsD5GqKP4OnWPQ==", "dev": true, "dependencies": { "dir-glob": "^3.0.1", @@ -3084,6 +3185,19 @@ "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", @@ -3359,10 +3473,13 @@ } }, "node_modules/get-tsconfig": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.5.0.tgz", - "integrity": "sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.6.0.tgz", + "integrity": "sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg==", "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, "funding": { "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } @@ -3651,22 +3768,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -3818,15 +3919,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -3887,15 +3979,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", @@ -3969,14 +4052,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true, - "funding": { - "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", @@ -3990,19 +4069,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -4030,12 +4096,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4197,9 +4257,9 @@ } }, "node_modules/lucide-react": { - "version": "0.248.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.248.0.tgz", - "integrity": "sha512-zMqVvwsvhLV6ooekfM8HEngR/lLGkIgOwZ4X140K+CDdAPwfp9LARnmJhMF32wlDganAIVEgN7saIv3pcCLUVw==", + "version": "0.252.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.252.0.tgz", + "integrity": "sha512-98hUdm23F3YlC3UN4mzv1FAsWr81YYdxF31cYhm19c51FwOph4dn5B4NjKp45UXBiR1Xx+cKrdmSIZX0ldS8zw==", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0" } @@ -4451,6 +4511,11 @@ "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", @@ -4498,10 +4563,20 @@ } } }, + "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.11", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.11.tgz", - "integrity": "sha512-+M0PwXeU80kRohZ3aT4J/OnR+l9/KD2nVLNNoRgFtnf+umQVFdGBAO2N8+nCnEi0xlh/Wk3zOGC+vNNx+uM79Q==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", "dev": true }, "node_modules/nopt": { @@ -4610,9 +4685,9 @@ } }, "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", "engines": { "node": ">= 6" } @@ -4626,22 +4701,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -4793,14 +4852,6 @@ "url": "https://github.com/sponsors/panva" } }, - "node_modules/openid-client/node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "engines": { - "node": ">= 6" - } - }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -4925,9 +4976,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "engines": { "node": ">= 6" } @@ -5057,18 +5108,18 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/preact": { - "version": "10.15.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.15.1.tgz", - "integrity": "sha512-qs2ansoQEwzNiV5eAcRT1p1EC/dmEzaATVDJNiB3g2sRDWdA7b7MurXdJjB2+/WQktGWZwxvDrnuRFbWuIr64g==", + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", + "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" } }, "node_modules/preact-render-to-string": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", - "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz", + "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==", "dependencies": { "pretty-format": "^3.8.0" }, @@ -5091,13 +5142,13 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "node_modules/prisma": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.16.0.tgz", - "integrity": "sha512-kSCwbTm3LCephyGfZMJYqBXpPJXdJStg5xwfzeFmR5C05zfkOURK9pQpJF6uUQvFWm3lI9ZMSNkObmFkAPnB+g==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.16.1.tgz", + "integrity": "sha512-C2Xm7yxHxjFjjscBEW4tmoraPHH/Vyu/A0XABdbaFtoiOZARsxvOM7rwc2iZ0qVxbh0bGBGBWZUSXO/52/nHBQ==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "4.16.0" + "@prisma/engines": "4.16.1" }, "bin": { "prisma": "build/index.js", @@ -5355,6 +5406,15 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5546,9 +5606,9 @@ } }, "node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5621,18 +5681,6 @@ "node": ">=0.10.0" } }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -5930,6 +5978,14 @@ "tailwindcss": ">=3.0.0 || insiders" } }, + "node_modules/tailwindcss/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -6034,9 +6090,9 @@ } }, "node_modules/tslib": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz", - "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -6059,6 +6115,11 @@ "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", @@ -6097,6 +6158,14 @@ "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", @@ -6221,6 +6290,18 @@ "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", @@ -6251,6 +6332,35 @@ "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", @@ -6291,21 +6401,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/which-typed-array": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", @@ -6348,15 +6443,23 @@ "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", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", "engines": { "node": ">= 14" } diff --git a/package.json b/package.json index 0353145beb3a0647d696de07c2e33b55b761bb27..54fe4a71f79f8db44b4a05968e6f6c0631cc0f79 100644 --- a/package.json +++ b/package.json @@ -8,13 +8,14 @@ "build": "next build", "start": "next start", "lint": "next lint", - "preview": "next build && next start", + "preview": "prisma generate && next build && next start", "vercel-build": "prisma generate && prisma migrate deploy && next build" }, "dependencies": { "@auth/prisma-adapter": "^1.0.0", "@hookform/resolvers": "^3.1.1", - "@prisma/client": "^4.16.0", + "@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", "@radix-ui/react-dialog": "^1.0.4", @@ -26,12 +27,14 @@ "@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", "bcrypt": "^5.1.0", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", - "lucide-react": "^0.248.0", + "dayjs": "^1.11.8", + "lucide-react": "^0.252.0", "next": "^13.4.7", "next-auth": "^4.22.1", "next-themes": "^0.2.1", @@ -49,14 +52,14 @@ "@tanstack/eslint-plugin-query": "^4.29.9", "@types/bcrypt": "^5.0.0", "@types/node": "^20.3.1", - "@types/react": "^18.2.13", + "@types/react": "^18.2.14", "@types/react-dom": "^18.2.6", "autoprefixer": "10.4.14", "eslint": "^8.43.0", "eslint-config-next": "^13.4.7", "postcss": "8.4.24", - "prisma": "^4.16.0", + "prisma": "^4.16.1", "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 341c014062452d6c924dd249dab700a2c0d64a16..bfc5153481d503701f6f2fe3b1ed07f724cbd894 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -9,21 +9,18 @@ datasource db { } model Account { - id String @id @default(cuid()) - userId String @unique - type String - provider String - providerAccountId String - refresh_token String? @db.Text - access_token String? @db.Text - expires_at Int? - token_type String? - scope String? - id_token String? @db.Text - session_state String? - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @default(now()) @map("updated_at") - refresh_token_expires_in Int? + id String @id @default(cuid()) + userId String @unique + type String + provider String + providerAccountId String + refresh_token String? @db.Text + access_token String? @db.Text + expires_at Int? + token_type String? + scope String? + id_token String? @db.Text + session_state String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@ -51,6 +48,11 @@ model VerificationToken { @@map("verification_tokens") } +enum UserRole { + ADMIN + USER +} + model User { id String @id @default(cuid()) name String @@ -59,6 +61,12 @@ model User { emailVerified DateTime? @map("email_verified") password String? image String? + banner String? + bio String? + website String? + location String? + private Boolean @default(false) + role UserRole @default(USER) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @default(now()) @map("updated_at") @@ -67,11 +75,11 @@ model User { playingGameList Int[] finishedGameList Int[] - accounts Account? - sessions Session? - - gweets Gweet[] - likes Like[] + accounts Account[] + sessions Session[] + gweets Gweet[] + regweets Regweet[] + likes Like[] following Follows[] @relation("following") followers Follows[] @relation("follower") @@ -93,23 +101,44 @@ model Follows { model Gweet { id String @id @default(cuid()) - userId String @map("user_id") + authorId String @map("user_id") content String - likeCount Int @default(0) @map("like_count") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @default(now()) @map("updated_at") - replyToUserId String? @map("reply_to_user_id") replyToGweetId String? @map("reply_to_gweet_id") + quoteGweetId String? @map("quote_gweet_id") + + regweets Regweet[] + media Media[] + likes Like[] - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + author User @relation(fields: [authorId], references: [id], onDelete: Cascade) comment Gweet? @relation("gweet_comment", fields: [replyToGweetId], references: [id]) allComments Gweet[] @relation("gweet_comment") - likes Like[] + quote Gweet? @relation("gweet_quote", fields: [quoteGweetId], references: [id]) + allQuotes Gweet[] @relation("gweet_quote") @@map("gweets") } +enum MediaTypes { + IMAGE + VIDEO + GIF +} + +model Media { + id String @id @default(cuid()) + url String + type MediaTypes @default(IMAGE) + gweetId String @map("gweet_id") + + gweet Gweet? @relation(fields: [gweetId], references: [id], onDelete: Cascade) + + @@map("media") +} + model Like { id String @id @default(cuid()) gweetId String @map("gweet_id") @@ -122,54 +151,24 @@ model Like { @@map("likes") } -model Hashtag { - id String @id @default(cuid()) - text String - hashtag String @unique - score Int @default(1) - created_at DateTime @default(now()) +model Regweet { + id String @id @default(cuid()) + gweetId String @map("gweet_id") + userId String @map("user_id") + createdAt DateTime @default(now()) @map("created_at") + + user User @relation(fields: [userId], references: [id]) + gweet Gweet @relation(fields: [gweetId], references: [id], onDelete: Cascade) + + @@map("regweets") } -// model Post { -// id String @id @default(cuid()) -// userId String -// content String -// likeCount Int? @default(0) -// createdAt DateTime @default(now()) @map("created_at") -// updatedAt DateTime @default(now()) @map("updated_at") - -// user User @relation(fields: [userId], references: [id], onDelete: Cascade) -// Comment Comment[] -// Like Like[] - -// @@map("posts") -// } - -// model Comment { -// id String @id @default(cuid()) -// createdAt DateTime @default(now()) @map("created_at") -// updatedAt DateTime @default(now()) @map("updated_at") -// message String -// likeCount Int? @default(0) -// postId String -// userId String - -// user User @relation(fields: [userId], references: [id], onDelete: Cascade) -// post Post @relation(fields: [postId], references: [id], onDelete: Cascade) -// Like Like[] - -// @@map("comments") -// } - -// model Like { -// id String @id @default(cuid()) -// postId String -// commentId String? -// userId String - -// user User @relation(fields: [userId], references: [id], onDelete: Cascade) -// post Post @relation(fields: [postId], references: [id], onDelete: Cascade) -// comment Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade) - -// @@map("likes") -// } +model Hashtag { + id String @id @default(cuid()) + text String + hashtag String @unique + score Int @default(1) + createdAt DateTime @default(now()) @map("created_at") + + @@map("hashtags") +}