diff --git a/app/(content)/(user)/[username]/following/page.tsx b/app/(content)/(user)/[username]/following/page.tsx index 5ea8f4daa5c5fef29b079a07c60284b12ab27cba..2208842f94471ab149b0c9fa949de636e98236f3 100644 --- a/app/(content)/(user)/[username]/following/page.tsx +++ b/app/(content)/(user)/[username]/following/page.tsx @@ -1,7 +1,26 @@ -export default async function Following() { +import { BackHeader } from "@/components/back-header" +import { GlobalLayout } from "@/components/global-layout" +import { UserFollows } from "@/components/profile/components/user-follows" +import { Card } from "@/components/ui/card" +import { getCurrentUser } from "@/lib/session" + +export default async function Following({ params }: { params: { username: string } }) { + const session = await getCurrentUser() + return ( - <div> - <h1>Following Page WIP</h1> - </div> + <GlobalLayout + mainContent={ + <Card className="w-full overflow-hidden "> + <div className="p-3"> + <BackHeader> + <h1 className="font-bold">Following</h1> + </BackHeader> + </div> + <div className="px-5"> + <UserFollows username={params.username} session={session} /> + </div> + </Card> + } + /> ) } \ No newline at end of file diff --git a/app/api/users/follow/route.ts b/app/api/users/follow/route.ts index 7e36eb5c10dac51dd32462b4c50a6f88623a9e27..5c4048d49b53cc7427c4e94344010cc8eef75630 100644 --- a/app/api/users/follow/route.ts +++ b/app/api/users/follow/route.ts @@ -3,14 +3,13 @@ import { getCurrentUser } from "@/lib/session" import { NextResponse } from "next/server" import { z } from "zod" -// get following or followers information export async function GET(request: Request) { const { searchParams } = new URL(request.url) - const userId = searchParams.get("userId") || undefined + const username = searchParams.get("userId") || undefined const type = searchParams.get("type") || undefined - const userIdSchema = z.string().cuid().optional() - const zod = userIdSchema.safeParse(userId) + const userIdSchema = z.string().optional() + const zod = userIdSchema.safeParse(username) if (!zod.success) { return NextResponse.json(zod.error, { status: 400 }) @@ -21,19 +20,29 @@ export async function GET(request: Request) { const followers = await db.user .findUnique({ where: { - id: userId, + username, + }, + }) + .followers({ + include: { + followers: true, + following: true, }, }) - .followers() return NextResponse.json(followers, { status: 200 }) } else if (type === "following") { const following = await db.user .findUnique({ where: { - id: userId, + username, + }, + }) + .following({ + include: { + followers: true, + following: true, }, }) - .following() return NextResponse.json(following, { status: 200 }) } @@ -42,7 +51,6 @@ export async function GET(request: Request) { } } -// follow a user export async function PUT(request: Request) { const session = await getCurrentUser() const { userId } = await request.json() @@ -84,7 +92,6 @@ export async function PUT(request: Request) { } } -// unfollow a user export async function DELETE(request: Request) { const session = await getCurrentUser() const { userId } = await request.json() diff --git a/components/follow-button.tsx b/components/follow-button.tsx index 294bf2700fc7194870e5a28903a44378897ee6e3..ed87a2975dc22480f794d14462f530777f66c7ee 100644 --- a/components/follow-button.tsx +++ b/components/follow-button.tsx @@ -36,43 +36,48 @@ export const FollowButton = ({ }, [router, followMutation.isSuccess, unfollowMutation.isSuccess]) return ( - <Dialog open={open} onOpenChange={setOpen}> - {isFollowing ? ( - <DialogTrigger asChild> - <Button variant={`${isHovered ? "destructive" : "outline"}`} size="lg" - className="w-24" - onMouseEnter={() => { - setText("Unfollow") - setIsHovered(true) - }} - onMouseOut={() => { - setText("Following") - setIsHovered(false) - }}> - {text} + <div onClick={(e) => { + e.preventDefault() + e.stopPropagation() + }}> + <Dialog open={open} onOpenChange={setOpen}> + {isFollowing ? ( + <DialogTrigger asChild > + <Button variant={`${isHovered ? "destructive" : "outline"}`} size="lg" + className="w-24" + onMouseEnter={() => { + setText("Unfollow") + setIsHovered(true) + }} + onMouseOut={() => { + setText("Following") + setIsHovered(false) + }}> + {text} + </Button> + </DialogTrigger> + ) : ( + <Button size="lg" onClick={() => followMutation.mutate({ userId })}> + Follow </Button> - </DialogTrigger> - ) : ( - <Button size="lg" onClick={() => followMutation.mutate({ userId })}> - Follow - </Button> - )} - <DialogContent> - <DialogHeader> - <DialogTitle>Unfollow @{username}?</DialogTitle> - <DialogDescription> - You can still view their profile, unless their Gweets are protected. - </DialogDescription> - </DialogHeader> - <DialogFooter> - <Button type="submit" onClick={() => unfollowMutation.mutate({ userId })} disabled={unfollowMutation.isLoading}> - {unfollowMutation.isLoading && ( - <Icons.spinner className="mr-2 h-4 w-4 animate-spin" /> - )} - Unfollow - </Button> - </DialogFooter> - </DialogContent> - </Dialog> + )} + <DialogContent> + <DialogHeader> + <DialogTitle>Unfollow @{username}?</DialogTitle> + <DialogDescription> + You can still view their profile, unless their Gweets are protected. + </DialogDescription> + </DialogHeader> + <DialogFooter> + <Button type="submit" onClick={() => unfollowMutation.mutate({ userId })} disabled={unfollowMutation.isLoading}> + {unfollowMutation.isLoading && ( + <Icons.spinner className="mr-2 h-4 w-4 animate-spin" /> + )} + Unfollow + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + </div> ) } \ No newline at end of file diff --git a/components/profile/api/get-follows.ts b/components/profile/api/get-follows.ts index dc83afb8de3fc7b2dc5c40f80186464b3f0ad418..bfb15781fd8e6246ab3bfba6b138e3c2c20680da 100644 --- a/components/profile/api/get-follows.ts +++ b/components/profile/api/get-follows.ts @@ -1,6 +1,6 @@ export const getFollows = async (id: string | undefined, type: string | undefined) => { try { - const data = await fetch(`/api/users/follow?type=${type}&user_id=${id}`) + const data = await fetch(`/api/users/follow?type=${type}&userId=${id}`) .then((result) => result.json()) return data diff --git a/components/profile/components/user-follows.tsx b/components/profile/components/user-follows.tsx new file mode 100644 index 0000000000000000000000000000000000000000..afd3d0fade42a736af50e264a4bef370a7eae3c7 --- /dev/null +++ b/components/profile/components/user-follows.tsx @@ -0,0 +1,62 @@ +"use client" + +import LoadingItem from "@/components/loading-item" +import { TryAgain } from "@/components/try-again" +import { User } from "next-auth" +import { usePathname } from "next/navigation" +import { useGetFollows } from "../hooks/use-get-follows" +import { UserItem } from "./user-item" + +export const UserFollows = ({ username, session }: { username: string, session: User | undefined }) => { + const pathValue = usePathname().split("/")[2] || "" + + const { + data: following, + isLoading, + isError, + } = useGetFollows({ + id: username, + type: pathValue, + }) + + if (isLoading) { + return ( + <LoadingItem /> + ) + } + + if (isError || !following) { + return ( + <TryAgain /> + ) + } + + if (following?.length === 0) { + if (session?.username === username) { + return ( + <div className="m-6 flex justify-center"> + <div className="font-bold"> + <h1>You are not following anyone.</h1> + <p>When you do, it'll show up here.</p> + </div> + </div> + ) + } + return ( + <div className="m-6 flex justify-center"> + <div className="font-bold"> + <h1><span className="text-sky-500">@{username}</span> is not following anyone yet.</h1> + <p>When they do, it'll show up here.</p> + </div> + </div> + ) + } + + return ( + <div className="space-y-5 flex flex-col"> + {following?.map((user) => { + return <UserItem key={user?.id} user={user} sessionId={session?.id} /> + })} + </div> + ) +} \ No newline at end of file diff --git a/components/profile/components/user-item.tsx b/components/profile/components/user-item.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8a8f81dba302e0a066c355f98ae5069516221803 --- /dev/null +++ b/components/profile/components/user-item.tsx @@ -0,0 +1,41 @@ +import { FollowButton } from "@/components/follow-button" +import { UserAvatar } from "@/components/user-avatar" +import Link from "next/link" +import { IUser } from "../types" +import { following } from "../utils/following" + +export const UserItem = ({ user, sessionId }: { user: IUser, sessionId: string | undefined }) => { + const isFollowing = following({ + user, + sessionUserId: sessionId ? sessionId : "", + }) + + return ( + <Link + href={`/${user?.username}`} + className="flex flex-row flex-shrink gap-3 hover:bg-accent active:bg-accent rounded-lg p-3 items-center"> + + <UserAvatar + user={{ username: user.username, image: user.image || null }} + className="h-12 w-12 aspect-square" + /> + + <div className="flex flex-col flex-shrink justify-center h-full w-full space-y-3 overflow-hidden"> + <div className="flex justify-between"> + <div className="whitespace-nowrap"> + <h1 className="font-bold">{user.name}</h1> + <h1 className="text-sm text-sky-500">@{user.username}</h1> + </div> + + <FollowButton + userId={user.id} + username={user.username ? user.username : ""} + isFollowing={isFollowing} + /> + </div> + + {user.bio ? (<p className="truncate w-full">{user.bio}</p>) : <p> </p>} + </div> + </Link> + ) +} diff --git a/components/trends/components/trend.tsx b/components/trends/components/trend.tsx index 2ce19c9b8c18a64ec4a0bc9d7c194a5f1e8b7f2c..a13ae5718754d1ba58e43d2e42ae4d1b4900ca3a 100644 --- a/components/trends/components/trend.tsx +++ b/components/trends/components/trend.tsx @@ -7,12 +7,12 @@ export const Trend = ({ ranking = 1, title, gweets = 1 }: iTrendProps) => { return ( <Link href={`/search?query=${title.toLowerCase()}`} - className="flex flex-col justify-between hover:bg-accent active:bg-accent rounded-lg p-1"> + className="flex flex-col justify-between hover:bg-accent active:bg-accent rounded-lg p-1 overflow-hidden"> <div className="text-xs text-muted-foreground"> {ranking} · Trending </div> - <div>#{title}</div> + <div className="truncate">#{title}</div> <div className="text-xs text-muted-foreground"> {gweets} {gweets === 1 ? "gweet" : "gweets"} </div>