diff --git a/app/(content)/(gaming)/games/[gameid]/page.tsx b/app/(content)/(gaming)/games/[gameid]/page.tsx index 65e4ba58f2a688ffccd15b19c7f588fe9e6d15f4..9decb17582e3cff2bd46fda773cd1937e10926b7 100644 --- a/app/(content)/(gaming)/games/[gameid]/page.tsx +++ b/app/(content)/(gaming)/games/[gameid]/page.tsx @@ -2,12 +2,12 @@ import AddGameToList from "@/components/addGameToList"; import { AspectRatio } from "@/components/ui/aspect-ratio"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; +import { db } from "@/lib/db"; import { getGame } from "@/lib/igdb"; import { getCurrentUser } from "@/lib/session"; import { formatDate } from "@/lib/utils"; import { IGame } from "@/types/igdb-types"; import Image from "next/image"; -import { db } from "@/lib/db"; // renders a single game detail page export default async function GameDetail({ params }: { params: { gameid: string } }) { @@ -22,9 +22,6 @@ export default async function GameDetail({ params }: { params: { gameid: string } }) - - /* console.log(user) */ - const companies = data[0].involved_companies.map((company) => { if (company !== data[0].involved_companies[0]) { return `, ${company.company.name}` @@ -33,9 +30,7 @@ export default async function GameDetail({ params }: { params: { gameid: string }) return ( - <div className="main-content h-full"> - <Card className="w-full h-full overflow-hidden"> <div className="h-64 overflow-hidden"> <AspectRatio ratio={889 / 500}> @@ -85,7 +80,10 @@ export default async function GameDetail({ params }: { params: { gameid: string </div> </div> <div className="px-6 md:px-12"> - {/* <div className="border-b border-gray-400 dark:border-gray-200" /> */} + <div className="border-b border-gray-400 dark:border-gray-200" /> + <div className="p-6 w-full flex justify-center"> + {user && <AddGameToList userGameList={fullUser?.favGameList!} gameId={params.gameid} />} + </div> {/* comments */} </div> </Card > @@ -108,7 +106,6 @@ export default async function GameDetail({ params }: { params: { gameid: string </div> </Card> </div> - <AddGameToList userGameList={fullUser?.favGameList!} gameId={params.gameid} /> </div > ) } \ No newline at end of file diff --git a/app/(content)/(home)/home/[postid]/page.tsx b/app/(content)/(home)/home/[postid]/page.tsx index f27b88f28b2fb2c99c755338150dbf2ea1b6da68..07d1c2a3fdad506dfcd0902b04fc59bbec4fc2c7 100644 --- a/app/(content)/(home)/home/[postid]/page.tsx +++ b/app/(content)/(home)/home/[postid]/page.tsx @@ -1,108 +1,19 @@ -import CommentButton from "@/components/comment-button"; -import LikeButton from "@/components/like-button"; -import PostCommentForm from "@/components/post-comment"; -import PostItem from "@/components/post-item"; -import { db } from "@/lib/db"; -import { Prisma } from "@prisma/client"; -export const revalidate = 5; // revalidate this page every 5 seconds - -type messageType = any // Prisma.PostUncheckedCreateInput -type messageItemProps = { - msg: messageType; -}; +// import { PostCommentForm } from "@/components/comment-gweets"; export default async function PostDetail({ params }: { params: { postid: string } }) { const postid = params.postid - let comments = null - let message = null - - try { - comments = await db.comment.findMany({ - where: { - postId: postid - }, - orderBy: { - createdAt: "desc" - }, - include: { - user: true, - Like: true - }, - }) - - message = await db.post.findUnique({ - where: { - id: postid - }, - include: { - user: true, - Comment: true, - Like: true, - }, - }) - - } catch (error) { - console.log("the database is not running, try: 'npx prisma migrate dev --name init' if you want to use the database") - } - - return ( - <div> - <h1>Post Section</h1> - <p>This will be where all comments show up.</p> - <p>Needs a reload after posting!!</p> - <PostItem msg={message} /> - - <PostCommentForm postid={postid} /> - - {comments ? - <> - {comments.map((msg) => ( - <CommentItem msg={msg} key={msg.id} /> - ))} - </> - : - <p>no comments / no database</p>} - </div> - ) -} - -const CommentItem = ({ msg }: any) => { - if (!msg.id) { - return <div></div>; - } return ( - <div className="flex border-b border-gray-200 py-4"> - <div className="flex-shrink-0"> - <div className="h-10 w-10 rounded-full bg-gray-300"></div> + <div className="main-content px-3 pb-3"> + <div className="lg:col-span-1"> + {/* <PostCommentForm postid={postid} /> */} </div> - <div className="ml-4 flex flex-col flex-grow"> - <div> - <div className="flex items-center"> - <span className="font-bold mr-2">{msg.user.name}</span> - <span className="text-gray-500 text-sm"> - {formatDate(new Date(msg.createdAt!))} - </span> - </div> - <div className="text-gray-800">{msg.message}</div> - </div> - <div className="mt-4 flex"> - <div className="bg-gray-200 rounded-lg py-10 px-20 mr-2"> - </div> - </div> - <div className="flex justify-end" > - <LikeButton data={msg} /> + <div className="side-content"> + <div className="flex-col"> + </div> </div> </div> - ); -}; - -function formatDate(date: Date) { - return date.toLocaleDateString("en-US", { - day: "numeric", - month: "short", - year: "numeric" - }); + ) } \ No newline at end of file diff --git a/app/(content)/(home)/home/page.tsx b/app/(content)/(home)/home/page.tsx index 02156d864cba762c6ae747408ac5199d205ab45c..dbffa82339cfcaf836d1c674bbf4c9795a4752c7 100644 --- a/app/(content)/(home)/home/page.tsx +++ b/app/(content)/(home)/home/page.tsx @@ -1,41 +1,16 @@ -import PostItem from "@/components/post-item"; -import { PostMessageForm } from "@/components/post-messages"; -import { Card } from "@/components/ui/card"; -import { db } from "@/lib/db"; +import { PostGweets } from "@/components/post-gweets"; export default async function HomePage() { - let messages = null - try { - messages = await db.post.findMany({ - orderBy: { - createdAt: "desc" - }, - include: { - user: true, - Comment: true, - Like: true - }, - }) - } catch (error) { - throw new Error("the database is not running, check your .env file") - } - return ( - // <div className="main-content px-3"> - <div className="relative md:gap-10 lg:grid lg:grid-cols-[1fr_240px] px-3"> - <PostMessageForm /> + <div className="main-content px-3 pb-3"> + <div className="lg:col-span-1"> + <PostGweets /> + </div> - <Card className="w-full h-full overflow-hidden p-6 md:p-12"> - {messages ? messages.map((msg) => ( - <PostItem msg={msg} key={msg.id} /> - )) - : - <p>There are no messages currently</p>} - </Card> + <div className="side-content"> + <div className="flex-col"> - {/* <div className="side-content"> */} - <div className="hidden lg:block flex-col"> - a + </div> </div> </div> ) diff --git a/app/(content)/(user)/[userid]/page.tsx b/app/(content)/(user)/[userid]/page.tsx index e59fb23e2e2758881bf78a2d596074c269f577c3..005a747b08e63eaae2389041057c9d8a6c306fdd 100644 --- a/app/(content)/(user)/[userid]/page.tsx +++ b/app/(content)/(user)/[userid]/page.tsx @@ -21,7 +21,10 @@ export default async function User({ params }: { params: { userid: string } }) { } }) - const favoritegames = await getFavoriteGames(fullUser?.favGameList!) + let favoritegames = undefined + if (fullUser?.favGameList?.length !== 0) { + favoritegames = await getFavoriteGames(fullUser?.favGameList!) + } return ( <div className="main-content h-full"> diff --git a/app/api/comments/route.ts b/app/api/comments/route.ts index 86001bf164177dd5cc2a60d1d072445afa0c5656..13e15c2429e8fa579d31cb6983eb92ba242d73fd 100644 --- a/app/api/comments/route.ts +++ b/app/api/comments/route.ts @@ -1,63 +1,66 @@ -import { authOptions } from "@/lib/auth"; import { db } from "@/lib/db"; -import { Prisma } from "@prisma/client"; -import { getServerSession } from "next-auth/next"; +import { getCurrentUser } from "@/lib/session"; import { revalidatePath } from "next/cache"; import { NextRequest, NextResponse } from "next/server"; -type comment = Prisma.CommentUncheckedCreateInput - export async function POST(req: NextRequest) { - const session = await getServerSession(authOptions); + const user = await getCurrentUser(); - if (!session) { - return NextResponse.json({ status: 401 }); + if (!user) { + return NextResponse.json({ status: 401, message: 'Unauthorized' }); } - const userId = session.user.id - const data = await req.json() - - console.log("router data: " + data.message, "status:") + const userId = user.id; + const content = await req.json() try { await db.comment.create({ data: { - message: data.message, - postId: data.postId, + message: content.gweet, + postId: content.postId, userId: userId, } }) - console.log("created") const path = req.nextUrl.searchParams.get('path') || '/'; revalidatePath(path); - return NextResponse.json({ status: 201, message: 'Message Created' }) + return NextResponse.json({ status: 201, message: 'Comment Created' }) } catch (error: any) { - console.log("fail" + error); + return NextResponse.json({ status: 500, message: error.message }) } - console.log("post") } -export async function GET(req: NextRequest, res: NextResponse) { +export async function GET(req: NextRequest): Promise<NextResponse> { + const pa = req.nextUrl.searchParams; + try { - const data = await req.json() - console.log("router data: " + data, "status:") - } catch (error) { + const p = pa.get('postid') - } + if (!p) { + return NextResponse.json({ status: 400, message: 'Bad Request' }) + } - try { - const messages = await db.comment.findMany({ + const message = await db.post.findUnique({ + where: { + id: p + }, + include: { + user: true, + }, + }) + + const comments = await db.comment.findMany({ + where: { + postId: p + }, orderBy: { createdAt: "desc" } }) - return NextResponse.json({ status: 200, messages: messages }) + return NextResponse.json(comments); } catch (error) { - console.log("fail" + error); - // res.status(400) + return NextResponse.json(error, { status: 500 }); } - console.log("get") } \ No newline at end of file diff --git a/app/api/gameList/route.ts b/app/api/gameList/route.ts index 0f7489ede768fe669546f0bfa207aee40da2daba..852db6b4ed80f7650a6ccd70e5eb35d005008b7b 100644 --- a/app/api/gameList/route.ts +++ b/app/api/gameList/route.ts @@ -13,11 +13,9 @@ export async function PUT(req: NextRequest) { const userId = user.id; const data = await req.json() data.gameId = parseInt(data.gameId) - console.log(data); - console.log(userId); + try { if (data.add) { - console.log("add true") await db.user.update({ where: { id: userId @@ -28,7 +26,6 @@ export async function PUT(req: NextRequest) { } } }) - console.log("gameList updated") } else { const user = await db.user.findFirst({ where: { diff --git a/app/api/messages/route.ts b/app/api/messages/route.ts index 2e6107bbb06b51eb24a0e57d43e774801b542b67..6eb3296d3a705d11704ef00d9fb3dd9fc7231caf 100644 --- a/app/api/messages/route.ts +++ b/app/api/messages/route.ts @@ -13,8 +13,6 @@ export async function POST(req: NextRequest) { const userId = user.id; const content = await req.json() - console.log(content); - console.log(userId); try { await db.post.create({ data: { @@ -30,4 +28,23 @@ export async function POST(req: NextRequest) { } catch (error: any) { return NextResponse.json({ status: 500, message: error.message }) } +} + +export async function GET() { + try { + const messages = await db.post.findMany({ + orderBy: { + createdAt: "desc" + }, + include: { + user: true, + Comment: true, + Like: true + }, + }) + + return NextResponse.json(messages); + } catch (error) { + return NextResponse.json(error, { status: 500 }); + } } \ No newline at end of file diff --git a/components/addGameToList.tsx b/components/addGameToList.tsx index c40c4b9abdbe8f995f85a4fee5e60211024947ba..d54fc5babf73b93ee695c90085a19c6078ae93ee 100644 --- a/components/addGameToList.tsx +++ b/components/addGameToList.tsx @@ -1,8 +1,8 @@ "use client" -import { Post, Prisma } from "@prisma/client"; import { useRouter } from "next/navigation"; -import { startTransition, useEffect, useState } from "react"; +import { startTransition } from "react"; +import { Button } from "./ui/button"; export default function AddGameToList(props: { userGameList: Number[], gameId: string }) { @@ -50,25 +50,24 @@ export default function AddGameToList(props: { userGameList: Number[], gameId: s let button = <div></div>; try { if (!props.userGameList.includes(parseFloat(props.gameId))) { - button = ( <form onSubmit={addGame}> - <button type="submit" className="mt-2 bg-gray-300 text-gray-800 px-4 py-2 rounded float-right"> - AddGame - </button> + <Button type="submit" size="lg"> + Add Game To List + </Button> </form> ) } else { button = ( <form onSubmit={removeGame}> - <button type="submit" className="mt-2 bg-gray-300 text-gray-800 px-4 py-2 rounded float-right"> - Remove Game - </button> + <Button type="submit" size="lg" variant={"secondary"}> + Remove Game From List + </Button> </form> ) } } catch (error) { - + throw new Error("Failed to fetch comments"); } return ( diff --git a/components/comment-button.tsx b/components/comment-button.tsx deleted file mode 100644 index 7a72cdec71b37b0028d97346f4c992200c7f94fc..0000000000000000000000000000000000000000 --- a/components/comment-button.tsx +++ /dev/null @@ -1,25 +0,0 @@ -"use client" - -import { Prisma } from "@prisma/client"; -import { useRouter } from "next/navigation"; -import { startTransition } from "react"; -import { Icons } from "./icons"; -import { Button } from "./ui/button"; -import Link from "next/link"; - -export default function CommentButton(props: { data: any }) { - - const postid = props.data.id - const replyCount = props.data.Comment.length - //const replyCount = props.data.likeCount - return ( - <div> - <Link href={`/home/${postid}`}> - <Button type="submit" variant="ghost" size="lg" className="float-right" > - {replyCount} - <Icons.messagecircle className="h-3 w-3" /> - </Button> - </Link> - </div> - ) -} \ No newline at end of file diff --git a/components/comment-gweets.tsx b/components/comment-gweets.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4ef3272389b2a9b969c2eb35c362c95ff1247dfb --- /dev/null +++ b/components/comment-gweets.tsx @@ -0,0 +1,172 @@ +// "use client" + +// import { zodResolver } from "@hookform/resolvers/zod"; +// import { useForm } from "react-hook-form"; +// import * as z from "zod"; + +// import { Button } from "@/components/ui/button"; +// import { +// Form, +// FormControl, +// FormDescription, +// FormField, +// FormItem, +// FormLabel, +// FormMessage, +// } from "@/components/ui/form"; + +// import { Textarea } from "@/components/ui/textarea"; +// import { toast } from "@/components/ui/use-toast"; +// import { FormEvent, Fragment, useEffect, useState } from "react"; + +// import CommentItem from "./comment-item"; +// import { Icons } from "./icons"; +// import { Card } from "./ui/card"; +// import { Separator } from "./ui/separator"; +// import { Skeleton } from "./ui/skeleton"; + +// const FormSchema = z.object({ +// gweet: z +// .string() +// .min(1, { message: "Come on post something...", }) +// .max(1000, { message: "Gweets cannot be more that 1000 characters.", }), +// }) + +// export function PostCommentForm(props: { postid: string }) { +// const [isGweetLoading, setIsGweetLoading] = useState<boolean>(false); +// const [isLoading, setIsLoading] = useState<boolean>(false); +// const [messages, setMessages] = useState<any[]>([]); + +// const form = useForm<z.infer<typeof FormSchema>>({ +// resolver: zodResolver(FormSchema), +// }) + +// async function onCommentGweet(data: z.infer<typeof FormSchema>) { +// setIsGweetLoading(true); + +// await fetch('/api/comments', { +// method: 'POST', +// body: JSON.stringify(props.postid) +// }) + +// toast({ +// title: "Your comment is being processed...", +// description: ( +// <pre className="mt-2 w-[340px] rounded-md bg-slate-600 p-4"> +// <code className="text-white">{JSON.stringify(data, null, 2)}</code> +// </pre> +// ), +// }) + +// setIsGweetLoading(false); +// form.setValue('gweet', ''); +// await fetchMessages(); +// } + +// async function fetchMessages() { +// setIsLoading(true); + +// try { +// const res = await fetch(`/api/comments?postid=${props.postid}`); + +// if (!res.ok) { +// throw new Error("Failed to fetch comments"); +// } + +// const data = await res.json(); + +// setMessages(data); +// } catch (error) { +// return toast({ +// variant: "destructive", +// title: "Uh oh! Something went wrong.", +// description: "Failed to fetch messages. Please try again.", +// }); +// } + +// setIsLoading(false); +// } + +// useEffect(() => { +// fetchMessages(); +// }, []); + +// async function onSubmit(event: FormEvent<HTMLFormElement>) { +// event.preventDefault(); +// await fetchMessages(); +// } + +// // console.log((messages[0] as IPost).user.image); +// return ( +// <div> +// {/* <PostItem msg={(messages[0])} /> */} +// <Form {...form}> +// <form onSubmit={form.handleSubmit(onCommentGweet)} className="space-y-6"> +// <FormField +// control={form.control} +// name="gweet" +// render={({ field }) => ( +// <FormItem> +// <FormLabel>Comment</FormLabel> +// <FormControl> +// <Textarea +// placeholder="What's on your mind?" +// className="resize-none" +// disabled={isGweetLoading} +// {...field} +// /> +// </FormControl> +// <FormDescription> +// Your comment will be public, and everyone can see them. +// </FormDescription> +// <FormMessage /> +// </FormItem> +// )} +// /> +// <Button type="submit" disabled={isGweetLoading}>Submit</Button> +// </form> +// </Form> +// <Card className="w-full h-full overflow-hidden p-6 md:p-12 mt-12"> +// <form onSubmit={onSubmit}> +// <Button disabled={isLoading} type="submit" className="w-full mb-6"> +// {isLoading && ( +// <Icons.spinner className="mr-2 h-4 w-4 animate-spin" /> +// )} +// Load More +// </Button> + +// {messages.length > 0 ? ( +// messages.map((message: any) => ( +// <Fragment key={message.id}> +// <CommentItem msg={message} /> +// <Separator className="mt-3 mb-6" /> +// </Fragment> +// )) +// ) : ( +// <> +// {Array.from({ length: 4 }, (_, i) => i + 1).map((i) => ( +// <> +// <div className="flex"> +// <Skeleton className="h-10 w-10 rounded-full" /> +// <div className="ml-4 flex flex-col flex-grow"> +// <div> +// <div className="flex items-center"> +// <div className="mx-auto w-full space-y-6"> +// <Skeleton className="h-[30px] w-1/4" /> +// <Skeleton className="h-[20px] w-2/3" /> +// <Skeleton className="h-[20px] w-full" /> +// </div> +// </div> +// </div> +// </div> +// </div> +// <Separator className="mt-3 mb-6" /> +// </> +// ))} +// </> +// )} +// </form> +// </Card> +// </div> +// ) +// } \ No newline at end of file diff --git a/components/comment-item.tsx b/components/comment-item.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0356db7a7e7e31bf3ed10dd84e26057ff562ffbc --- /dev/null +++ b/components/comment-item.tsx @@ -0,0 +1,27 @@ +// import { formatTimeElapsed } from "@/lib/utils"; +// import { UserAvatar } from "./user-avatar"; + +// export default function CommentItem({ msg }: { msg: any }) { +// return ( +// <div className="flex"> +// <UserAvatar +// user={{ name: msg.user.username || null, image: msg.user.image || null }} +// 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">{msg.user.name}</h1> +// <h1 className="text-sky-500 text-sm"> +// @{msg.user.username} +// </h1> +// <h1 className="text-gray-500 text-sm ml-auto"> +// {formatTimeElapsed(msg.createdAt)} +// </h1> +// </div> +// <h1>{msg.message}</h1> +// </div> +// </div> +// </div> +// ) +// } \ No newline at end of file diff --git a/components/like-button.tsx b/components/like-button.tsx deleted file mode 100644 index 31c36cf7343fd445c617130572503591bd40fa94..0000000000000000000000000000000000000000 --- a/components/like-button.tsx +++ /dev/null @@ -1,84 +0,0 @@ -"use client" - -import { Prisma } from "@prisma/client"; -import { useRouter } from "next/navigation"; -import { startTransition } from "react"; -import { Icons } from "./icons"; -import { Button } from "./ui/button"; - -type likeType = Prisma.LikeUncheckedCreateInput -type postType = Prisma.PostUncheckedCreateInput -type commentType = Prisma.CommentUncheckedCreateInput -//type commentWithLikes = Prisma.CommentGetPayload<typeof commentWithPosts> - -export default function LikeButton(props: { data: any }) { - const router = useRouter(); - const likeCount = props.data.Like.length - /* const likeCount = props.data.likeCount */ - const likeArray = props.data.Like - /* const likeCount = countLikes(likeArray, props.data); */ - - - - - - async function postLike(e: any) { - e.preventDefault() - const postLikeData = props.data; - const likeData = {} as likeType - - if (postLikeData.postId == undefined) { - likeData.postId = postLikeData.id! - } else { - likeData.postId = postLikeData.postId - likeData.commentId = postLikeData.id - } - likeData.userId = postLikeData.userId - console.log(likeData.commentId) - - const response = await fetch('http://localhost:3000/api/likes', { - method: 'PUT', - body: JSON.stringify(likeData) - }) - - startTransition(() => { - // Refresh the current route and fetch new data from the server without - // losing client-side browser or React state. - router.refresh(); - }); - return await response.json() - } - - return ( - <div> - <form onSubmit={postLike}> - <Button type="submit" variant="ghost" size="lg" className="float-right" > - {likeCount} - <Icons.heart className="h-3 w-3" /> - </Button> - </form> - </div> - ) -} - -function countLikes(likeArray: any, msg: any): number { - let likeCount = 0; - - if (msg.postId == undefined) { - likeArray.forEach(function (like: any) { - if (like.postId == undefined) { - likeCount++; - } - }) - } else { - likeArray.forEach(function (like: any) { - if (like.postId != undefined) { - likeCount++; - } - }) - } - - - return likeCount; -} - diff --git a/components/post-comment-button.tsx b/components/post-comment-button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..369528dff647d90a55c894c051ad0a9232fb8c78 --- /dev/null +++ b/components/post-comment-button.tsx @@ -0,0 +1,17 @@ +// import Link from "next/link"; +// import { Icons } from "./icons"; +// import { Button } from "./ui/button"; + +// export default function CommentButton(props: { data: any }) { +// const postid = props.data.id +// const replyCount = props.data.Comment.length + +// return ( +// <Link href={`/home/${postid}`}> +// <Button type="submit" variant="ghost" size="lg" className="px-6 py-3" > +// <span className="pr-1">{replyCount}</span> +// <Icons.messagecircle className="h-5 w-5" /> +// </Button> +// </Link> +// ) +// } \ No newline at end of file diff --git a/components/post-comment.tsx b/components/post-comment.tsx deleted file mode 100644 index 6f80ff3de18c341db3d554f2c21dfcbe7a2fd309..0000000000000000000000000000000000000000 --- a/components/post-comment.tsx +++ /dev/null @@ -1,64 +0,0 @@ -"use client" - -import { Post, Prisma } from "@prisma/client"; -import { useRouter } from "next/navigation"; -import { startTransition, useEffect, useState } from "react"; - -type commentType = Prisma.CommentUncheckedCreateInput - -export default function PostCommentForm(props: { postid: string }) { - - const [formData, setFormData] = useState<commentType>({ message: "" } as commentType); - const router = useRouter(); - const postid = props.postid - - async function postComment(e: any) { - e.preventDefault() - - formData.postId = postid; - - const response = await fetch('http://localhost:3000/api/comments', { - method: 'POST', - body: JSON.stringify(formData) - }) - - startTransition(() => { - // Refresh the current route and fetch new data from the server without - // losing client-side browser or React state. - router.refresh(); - }); - return await response.json() - } - - const characterCount = formData.message.length; - const isOverLimit = characterCount >= 1000; - - const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { - const { value } = e.target; - setFormData({ ...formData, message: value }); - }; - - return ( - <div className="p-4 pb-20"> - <form onSubmit={postComment}> - <textarea - placeholder="Write something..." - name="message" - value={formData.message} - onChange={handleInputChange} - className="w-full p-2 border border-gray-600 rounded-xl resize-none" - rows={5} - maxLength={1000} - ></textarea> - <div className="flex justify-end mt-2"> - <span className={`${isOverLimit ? "text-red-500" : "text-gray-500"} text-sm`}> - {characterCount}/{1000} - </span> - </div> - <button type="submit" className="mt-2 bg-gray-300 text-gray-800 px-4 py-2 rounded float-right"> - Post - </button> - </form> - </div> - ) -} \ No newline at end of file diff --git a/components/post-gweets.tsx b/components/post-gweets.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8dd8738d6e465aec9192f592ae066785e77ce735 --- /dev/null +++ b/components/post-gweets.tsx @@ -0,0 +1,178 @@ +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import * as z from "zod" + +import { Button } from "@/components/ui/button" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" + +import { Textarea } from "@/components/ui/textarea" +import { toast } from "@/components/ui/use-toast" + +import { useSession } from "next-auth/react" +import { FormEvent, Fragment, useEffect, useState } from "react" +import { Icons } from "./icons" +import PostItem from "./post-item" +import { Card } from "./ui/card" +import { Separator } from "./ui/separator" +import { Skeleton } from "./ui/skeleton" + +const FormSchema = z.object({ + gweet: z + .string() + .min(1, { message: "Come on post something...", }) + .max(1000, { message: "Gweets cannot be more that 1000 characters.", }), +}) + +export function PostGweets() { + const [isGweetLoading, setIsGweetLoading] = useState<boolean>(false); + const [isLoading, setIsLoading] = useState<boolean>(false); + const [messages, setMessages] = useState<any[]>([]); + + const user = useSession(); + + useEffect(() => { + fetchMessages(); + }, []); + + const form = useForm<z.infer<typeof FormSchema>>({ + resolver: zodResolver(FormSchema), + }) + + async function onGweet(data: z.infer<typeof FormSchema>) { + setIsGweetLoading(true); + + await fetch('/api/messages', { + method: 'POST', + body: JSON.stringify(data), + next: { tags: ['collection'] } + }) + + toast({ + title: "Your gweet is being processed...", + description: ( + <pre className="mt-2 w-[340px] rounded-md bg-slate-600 p-4"> + <code className="text-white">{JSON.stringify(data, null, 2)}</code> + </pre> + ), + }) + + setIsGweetLoading(false); + form.setValue('gweet', ''); + await fetchMessages(); + } + + async function fetchMessages() { + setIsLoading(true); + + try { + const res = await fetch(`/api/messages`); + + if (!res.ok) { + throw new Error("Failed to fetch messages"); + } + + const data = await res.json(); + + setMessages(data); + } catch (error) { + return toast({ + variant: "destructive", + title: "Uh oh! Something went wrong.", + description: "Failed to fetch messages. Please try again.", + }); + } + + setIsLoading(false); + } + + async function onSubmit(event: FormEvent<HTMLFormElement>) { + event.preventDefault(); + await fetchMessages(); + } + + return ( + <div> + <Form {...form}> + <form onSubmit={form.handleSubmit(onGweet)} className="space-y-6"> + <FormField + control={form.control} + name="gweet" + render={({ field }) => ( + <FormItem> + <FormLabel>Gweet</FormLabel> + <FormControl> + <Textarea + placeholder="What's on your mind?" + className="resize-none" + disabled={isGweetLoading || !user.data?.user} + {...field} + /> + </FormControl> + <FormDescription> + Your gweets will be public, and everyone can see them. + </FormDescription> + <FormMessage /> + </FormItem> + )} + /> + <Button type="submit" disabled={isGweetLoading || !user.data?.user}>Submit</Button> + </form> + </Form> + <Card className="w-full h-full overflow-hidden p-6 md:p-12 mt-12"> + <form onSubmit={onSubmit}> + <Button disabled={isLoading} type="submit" className="w-full mb-6"> + {isLoading && ( + <Icons.spinner className="mr-2 h-4 w-4 animate-spin" /> + )} + Load More + </Button> + + {messages.length > 0 ? ( + messages.map((message: any) => ( + <Fragment key={message.id}> + <PostItem msg={message} /> + <Separator className="mt-3 mb-6" /> + </Fragment> + )) + ) : ( + <> + {Array.from({ length: 4 }, (_, i) => i + 1).map((i) => ( + <> + <div className="flex"> + <Skeleton className="h-10 w-10 rounded-full" /> + <div className="ml-4 flex flex-col flex-grow"> + <div> + <div className="flex items-center"> + <div className="mx-auto w-full space-y-6"> + <Skeleton className="h-[30px] w-1/4" /> + <Skeleton className="h-[20px] w-2/3" /> + <Skeleton className="h-[20px] w-full" /> + </div> + </div> + </div> + <div className="flex justify-end space-x-3 mt-3" > + <Skeleton key={i} className="h-10 w-20 rounded-full" /> + <Skeleton key={i} className="h-10 w-20 rounded-full" /> + </div> + </div> + </div> + <Separator className="mt-3 mb-6" /> + </> + ))} + </> + )} + </form> + </Card> + </div> + ) +} \ No newline at end of file diff --git a/components/post-item.tsx b/components/post-item.tsx index 0588a4b03e64237e8d56114cf896487b69f4c4e0..bd2b5482c5e18618a69652a345db181085faaaf4 100644 --- a/components/post-item.tsx +++ b/components/post-item.tsx @@ -1,35 +1,34 @@ -import { formatDate } from "@/lib/utils"; -import CommentButton from "./comment-button"; -import LikeButton from "./like-button"; +import { formatTimeElapsed } from "@/lib/utils"; +import { IPost } from "@/types/prisma-item"; +// import CommentButton from "./post-comment-button"; +// import LikeButton from "./post-like-button"; +import { UserAvatar } from "./user-avatar"; -export default function PostItem({ msg }: { msg: any }) { - if (!msg.id) { - return <div></div>; - } +export default function PostItem({ msg }: { msg: IPost }) { return ( - <div className="flex border-b border-gray-200 py-4"> - <div className="flex-shrink-0"> - <div className="h-10 w-10 rounded-full bg-gray-300"></div> - </div> + <div className="flex"> + <UserAvatar + user={{ name: msg.user.username || null, image: msg.user.image || null }} + className="h-10 w-10" + /> <div className="ml-4 flex flex-col flex-grow"> <div> <div className="flex items-center"> - <span className="font-bold mr-2">{msg.user.name}</span> - <span className="text-gray-500 text-sm"> - {formatDate(msg.createdAt)} - </span> - </div> - <div className="text-gray-800">{msg.content}</div> - </div> - <div className="mt-4 flex"> - <div className="bg-gray-200 rounded-lg py-10 px-20 mr-2"> + <h1 className="font-bold mr-2">{msg.user.name}</h1> + <h1 className="text-sky-500 text-sm"> + @{msg.user.username} + </h1> + <h1 className="text-gray-500 text-sm ml-auto"> + {formatTimeElapsed(msg.createdAt)} + </h1> </div> + <h1>{msg.content}</h1> </div> - <div className="flex justify-end" > + {/* <div className="flex justify-end" > <LikeButton data={msg} /> <CommentButton data={msg} /> - </div> + </div> */} </div> </div> ) -}; \ No newline at end of file +} \ No newline at end of file diff --git a/components/post-like-button.tsx b/components/post-like-button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4a8d923bd28d8278d4647c392e44fa3dcc38705d --- /dev/null +++ b/components/post-like-button.tsx @@ -0,0 +1,75 @@ +// "use client" + +// import { Prisma } from "@prisma/client"; +// import { useRouter } from "next/navigation"; +// import { startTransition } from "react"; +// import { Icons } from "./icons"; +// import { Button } from "./ui/button"; + +// type likeType = Prisma.LikeUncheckedCreateInput +// type postType = Prisma.PostUncheckedCreateInput +// type commentType = Prisma.CommentUncheckedCreateInput +// // type commentWithLikes = Prisma.CommentGetPayload<typeof commentWithPosts> + +// export default function LikeButton(props: { data: any }) { +// const router = useRouter(); +// const likeCount = props.data.Like.length +// // const likeCount = countLikes(likeArray, props.data); + +// async function postLike(e: any) { +// e.preventDefault() +// const postLikeData = props.data; +// const likeData = {} as likeType + +// if (postLikeData.postId == undefined) { +// likeData.postId = postLikeData.id! +// } else { +// likeData.postId = postLikeData.postId +// likeData.commentId = postLikeData.id +// } +// likeData.userId = postLikeData.userId + +// const response = await fetch('/api/likes', { +// method: 'PUT', +// body: JSON.stringify(likeData) +// }) + +// startTransition(() => { +// // Refresh the current route and fetch new data from the server without +// // losing client-side browser or React state. +// router.refresh(); +// }); +// return await response.json() +// } + +// return ( +// <form onSubmit={postLike}> +// <Button type="submit" variant="ghost" size="lg" className="px-6 py-3" > +// <span className="pr-1">{likeCount}</span> +// <Icons.heart className="h-5 w-5" /> +// </Button> +// </form> +// ) +// } + +// function countLikes(likeArray: any, msg: any): number { +// let likeCount = 0; + +// if (msg.postId == undefined) { +// likeArray.forEach(function (like: any) { +// if (like.postId == undefined) { +// likeCount++; +// } +// }) +// } else { +// likeArray.forEach(function (like: any) { +// if (like.postId != undefined) { +// likeCount++; +// } +// }) +// } + + +// return likeCount; +// } + diff --git a/components/post-messages.tsx b/components/post-messages.tsx deleted file mode 100644 index 727aa06f6f19683d39a1a8e22669c1b46afa16ef..0000000000000000000000000000000000000000 --- a/components/post-messages.tsx +++ /dev/null @@ -1,77 +0,0 @@ -"use client" - -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import * as z from "zod" - -import { Button } from "@/components/ui/button" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" - -import { Textarea } from "@/components/ui/textarea" -import { toast } from "@/components/ui/use-toast" - -const FormSchema = z.object({ - gweet: z - .string() - .min(1, { message: "Come on post something...", }) - .max(1000, { message: "Gweets cannot be more that 1000 characters.", }), -}) - -export function PostMessageForm() { - const form = useForm<z.infer<typeof FormSchema>>({ - resolver: zodResolver(FormSchema), - }) - - async function onSubmit(data: z.infer<typeof FormSchema>) { - await fetch('/api/messages', { - method: 'POST', - body: JSON.stringify(data), - next: { tags: ['collection'] } - }) - - toast({ - title: "Your gweet is being processed...", - description: ( - <pre className="mt-2 w-[340px] rounded-md bg-slate-600 p-4"> - <code className="text-white">{JSON.stringify(data, null, 2)}</code> - </pre> - ), - }) - } - - return ( - <Form {...form}> - <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6"> - <FormField - control={form.control} - name="gweet" - render={({ field }) => ( - <FormItem> - <FormLabel>Gweet</FormLabel> - <FormControl> - <Textarea - placeholder="What's on your mind?" - className="resize-none" - {...field} - /> - </FormControl> - <FormDescription> - Your gweets will be public, and everyone can see them. - </FormDescription> - <FormMessage /> - </FormItem> - )} - /> - <Button type="submit">Submit</Button> - </form> - </Form> - ) -} \ No newline at end of file diff --git a/components/ui/separator.tsx b/components/ui/separator.tsx new file mode 100644 index 0000000000000000000000000000000000000000..12d81c4a853472948b6dd2e85eff99b5d12fe60b --- /dev/null +++ b/components/ui/separator.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import * as SeparatorPrimitive from "@radix-ui/react-separator" + +import { cn } from "@/lib/utils" + +const Separator = React.forwardRef< + React.ElementRef<typeof SeparatorPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> +>( + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref + ) => ( + <SeparatorPrimitive.Root + ref={ref} + decorative={decorative} + orientation={orientation} + className={cn( + "shrink-0 bg-border", + orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", + className + )} + {...props} + /> + ) +) +Separator.displayName = SeparatorPrimitive.Root.displayName + +export { Separator } diff --git a/lib/igdb.ts b/lib/igdb.ts index d2b94af8234be79d80a1805e727e193856524575..e2354d61fafbbfc0a9c95e37a2ab56f5c25d1b25 100644 --- a/lib/igdb.ts +++ b/lib/igdb.ts @@ -104,9 +104,6 @@ export async function getGame(id: number): Promise<IGame[]> { export async function getFavoriteGames(gamelist: Number[]): Promise<IGame[]> { const auth = await getToken(); const url = new URL(`${IGDB_BASE_URL}/games`); - let gamelistString = gamelist.toString() - - console.log("ID STRING:", gamelistString) const response = await fetch(url, { method: 'POST', @@ -116,12 +113,11 @@ export async function getFavoriteGames(gamelist: Number[]): Promise<IGame[]> { }, body: `fields name, cover.image_id; limit ${limit}; - where id = (${gamelistString}); + where id = (${gamelist.toString()}); ` }); - console.log(response) - if (!response.ok) { + if (!response.ok) { throw new Error(`Error fetching games: ${response.statusText}`); } diff --git a/lib/utils.ts b/lib/utils.ts index a4563b1d39ecf754f6556c4d55a8ab1cb5cd0c74..e467e9a31a883d5cc35e047e8c7a8c4e6b3237bc 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -25,4 +25,23 @@ export function formatDate(data: number) { day: 'numeric', year: 'numeric' }) -} \ No newline at end of file +} + +export function formatTimeElapsed(createdAt: Date) { + const now = new Date(); + const timeDiff = Math.abs(now.getTime() - new Date(createdAt).getTime()); // Difference in milliseconds + const seconds = Math.floor(timeDiff / 1000); // Convert to seconds + const minutes = Math.floor(seconds / 60); // Convert to minutes + const hours = Math.floor(minutes / 60); // Convert to hours + const days = Math.floor(hours / 24); // Convert to days + + if (days > 0) { + return new Date(createdAt).toLocaleDateString(); // Show the date if days have passed + } else if (hours > 0) { + return hours + 'h'; // Show hours if hours have passed + } else if (minutes > 0) { + return minutes + 'm'; // Show minutes if minutes have passed + } else { + return seconds + 's'; // Show seconds if seconds have passed + } +} diff --git a/package-lock.json b/package-lock.json index 4c363f412d49ba27721a1e350b0c2c5ce2a1f7d6..fd7938abf9f86e0122d103aa756c1ed647d1923d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-scroll-area": "^1.0.4", "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.4", "@t3-oss/env-nextjs": "^0.4.0", @@ -1107,6 +1108,29 @@ } } }, + "node_modules/@radix-ui/react-separator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz", + "integrity": "sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", diff --git a/package.json b/package.json index ef93b2320bb4e31b6054c51b24610d6eb37b85d4..01b69ff927452eacabc099ecd42b78dea67678d5 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-scroll-area": "^1.0.4", "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.4", "@t3-oss/env-nextjs": "^0.4.0", diff --git a/types/prisma-item.d.ts b/types/prisma-item.d.ts index e281755f96d52a02d66ee99d7b1267165d33f7cd..c2feefe712613b215d8d5a01a442aaeb4a1d47fd 100644 --- a/types/prisma-item.d.ts +++ b/types/prisma-item.d.ts @@ -1,9 +1,11 @@ import { Comment, Like, Post, User } from "@prisma/client"; -export interface IPost { - user: Post & { - user: User; - Comment: Comment[]; - Like: Like[]; - } +export interface IPost extends Post { + user: User; + comments: Comment[]; + likes: Like[]; +} + +export interface IComment extends Comment { + user: User; } \ No newline at end of file