diff --git a/app/(content)/(user)/[userid]/followers/page.tsx b/app/(content)/(user)/[userid]/followers/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a024dd2d33ce785d357b497bb47fde72553d5230 --- /dev/null +++ b/app/(content)/(user)/[userid]/followers/page.tsx @@ -0,0 +1,17 @@ +import FollowersList from "@/components/following-users"; +import { useSession } from "next-auth/react"; + +export default function Followers() { + const { data: session } = useSession(); + + if (!session) { + return <div>Loading...</div>; + } + + return ( + <div> + <h1>Followers Page WIP</h1> + <FollowersList userId={session.user?.id} /> + </div> + ) +} \ No newline at end of file diff --git a/app/(content)/(user)/notifications/page.tsx b/app/(content)/(user)/[userid]/notifications/page.tsx similarity index 100% rename from app/(content)/(user)/notifications/page.tsx rename to app/(content)/(user)/[userid]/notifications/page.tsx diff --git a/app/(content)/(user)/followers/page.tsx b/app/(content)/(user)/followers/page.tsx deleted file mode 100644 index bf352f844956a40af55fe6c3ee73867628a9ab10..0000000000000000000000000000000000000000 --- a/app/(content)/(user)/followers/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export default function Followers() { - return ( - <div> - <h1>Followers Page WIP</h1> - </div> - ) -} \ No newline at end of file diff --git a/components/following-button.tsx b/components/following-button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..af3f477154482518863ab563d5ebcb1fa11cf031 --- /dev/null +++ b/components/following-button.tsx @@ -0,0 +1,54 @@ +"use client" + +import { PrismaClient } from '@prisma/client'; +import { useState } from 'react'; +import { Button } from './ui/button'; + +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 new file mode 100644 index 0000000000000000000000000000000000000000..be7c652973df7a5f4d0e5bb4871cfb8b71184eee --- /dev/null +++ b/components/following-users.tsx @@ -0,0 +1,46 @@ +"use client" + +import { PrismaClient } from '@prisma/client'; +import { useEffect, useState } from 'react'; + +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) => { + 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/user-item.tsx b/components/user-item.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0da145d8bdee65dbe40671248f0b64cfcc3106ec --- /dev/null +++ b/components/user-item.tsx @@ -0,0 +1,23 @@ +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/prisma/schema.prisma b/prisma/schema.prisma index a6b261ad022b8097f26e23533fea0e740a542bfd..46c6b9977670490988f4a0930f68e937f4cbd2de 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -22,6 +22,19 @@ model User { Post Post[] Comment Comment[] Like Like[] + + followers Follows[] @relation("follower") + following Follows[] @relation("following") +} + +model Follows { + follower User @relation("following", fields: [followerId], references: [id]) + followerId Int + following User @relation("follower", fields: [followingId], references: [id]) + followingId Int + createdAt DateTime @default(now()) + + @@id([followerId, followingId]) } model Post { diff --git a/types/next-auth.d.ts b/types/next-auth.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4d9a09eedc92a4aba1241db23ca0573cfd3cc78 --- /dev/null +++ b/types/next-auth.d.ts @@ -0,0 +1,12 @@ +import 'next-auth'; + +declare module 'next-auth' { + interface Session { + user: { + id: number; + name?: string | null; + email?: string | null; + image?: string | null; + }; + } +} \ No newline at end of file