Skip to content

Commit

Permalink
✨ feat: allow users to follow other users
Browse files Browse the repository at this point in the history
  • Loading branch information
MakakWasTaken committed Feb 8, 2024
1 parent 77e0790 commit 99fff87
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 77 deletions.
16 changes: 7 additions & 9 deletions pages/api/database/mealplan/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,15 @@ const handleGET = async (
// Get the user's meal plan.
const response = await prisma.mealPlan.findMany({
where: {
OR: [
{
// If we are a follower of the meal plan
followers: {
some: {
id: session.user.id,
},
public: true,
owner: {
followers: {
some: {
// If we are following the owner, show their meal plan
followerId: session.user.id,
},
public: true,
},
],
},
},
include: {
owner: true,
Expand Down
79 changes: 79 additions & 0 deletions pages/api/database/user/[id]/follow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import prisma from '#pages/api/_base'
import { authOptions } from '#pages/api/auth/[...nextauth]'
import { NextApiRequest, NextApiResponse } from 'next'
import { Session, getServerSession } from 'next-auth'

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const session = await getServerSession(req, res, authOptions)

if (!session) {
res.status(401).json({
ok: false,
message: 'Not authenticated',
})
return
}

if (req.method === 'PUT') {
await handlePUT(req, res, session)
} else {
res.status(405).json({
ok: false,
message: 'Method not allowed',
})
}
} catch (err) {
res.status(500).json({
ok: false,
message: err.message ?? err,
})
}
}

const handlePUT = async (
req: NextApiRequest,
res: NextApiResponse,
session: Session,
) => {
const id = req.query.id as string

const exists = await prisma.follows.findFirst({
where: {
followerId: session.user.id,
followingId: id,
},
})

if (exists) {
// If the value exists, delete it
await prisma.follows.delete({
where: {
followerId_followingId: {
followerId: session.user.id,
followingId: id,
},
},
})
res.json({
ok: true,
message: 'Succesfully updated following state',
data: false,
})
} else {
// Create it
await prisma.follows.create({
data: {
followerId: session.user.id,
followingId: id,
},
})
res.json({
ok: true,
message: 'Succesfully updated following state',
data: true,
})
}
}

export default handler
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,30 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
select: {
id: true,
feeditems: true,
followers: true,
following: true,
followers: {
include: {
following: {
select: {
id: true,
name: true,
image: true,
created: true,
},
},
},
},
following: {
include: {
follower: {
select: {
id: true,
name: true,
image: true,
created: true,
},
},
},
},
image: true,
name: true,
ratings: true,
Expand All @@ -48,7 +70,11 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
return res.json({
ok: true,
message: 'Succesfully got user',
data: user,
data: {
...user,
followers: user.followers.map((follower) => follower.following),
following: user.following.map((follower) => follower.follower),
},
})
}

Expand Down
1 change: 1 addition & 0 deletions pages/recipe/[recipeId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ const RecipePage: FC<RecipePageProps> = ({ recipe, user }) => {

export const getServerSideProps: GetServerSideProps = async (context) => {
try {
console.log(`database/recipe/${context.params?.recipeId}`)
const recipe = await api.get<YKResponse<['recipe']>>(
`database/recipe/${context.params?.recipeId}`,
)
Expand Down
48 changes: 42 additions & 6 deletions pages/user/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ import {
Tabs,
Typography,
} from '@mui/material'
import { FeedItem, Rating, User } from '@prisma/client'
import { FeedItem, Follows, Rating, User } from '@prisma/client'
import { DateTime } from 'luxon'
import { GetServerSideProps } from 'next'
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import { Session, getServerSession } from 'next-auth'
import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { NextSeo, ProfilePageJsonLd } from 'next-seo'
import Image from 'next/image'
import { FC, useState } from 'react'
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
import { toast } from 'sonner'

type PublicUser = Pick<User, 'id' | 'image' | 'name' | 'created'>

Expand All @@ -43,18 +44,42 @@ interface UserPageProps {
}
}

const UserPage: FC<UserPageProps> = ({ ownUser, user }) => {
const UserPage: FC<InferGetServerSidePropsType<typeof getServerSideProps>> = ({
ownUser,
user,
}) => {
const { t } = useTranslation('common')

const [tab, setTab] = useState(0)
const [followDialogOpen, setFollowDialogOpen] = useState<
null | 'followers' | 'following'
>(null)

const [following, setFollowing] = useState(
user.followers.some((follower) => follower),
)

const followClick = (type: 'followers' | 'following') => {
setFollowDialogOpen(type)
}

const updateFollowState = async () => {
try {
setFollowing((prev) => !prev)
const response = await api.put<YKResponse<boolean>>(
`database/user/${user.id}/follow`,
)

console.log(response.data.data)

setFollowing(response.data.data)
} catch (err) {
// Reset the following state
setFollowing((prev) => !prev)
toast.error(err.message ?? err)
}
}

return (
<Box
sx={{
Expand Down Expand Up @@ -92,7 +117,7 @@ const UserPage: FC<UserPageProps> = ({ ownUser, user }) => {
<DialogContent>
{/* Show list of selected type of follow */}
<List>
{user[followDialogOpen as 'followers' | 'following'].map(
{user[followDialogOpen as 'followers' | 'following']?.map(
(followUser) => (
<Link href={`/user/${followUser.id}`} key={followUser.id}>
<ListItem>
Expand Down Expand Up @@ -135,6 +160,14 @@ const UserPage: FC<UserPageProps> = ({ ownUser, user }) => {
)}`}
/>
</Box>
<Button
variant="contained"
sx={{ my: 1 }}
color={following ? 'secondary' : 'primary'}
onClick={updateFollowState}
>
{following ? t('following') : t('follow')}
</Button>
<Box sx={{ display: 'flex' }}>
<Button onClick={() => followClick('followers')}>
<Box sx={{ textAlign: 'center' }}>
Expand Down Expand Up @@ -209,7 +242,9 @@ const UserPage: FC<UserPageProps> = ({ ownUser, user }) => {
)
}

export const getServerSideProps: GetServerSideProps = async (context) => {
export const getServerSideProps: GetServerSideProps<UserPageProps> = async (
context,
) => {
try {
const userResponse = await api.get<YKResponse<User>>(
`database/user/${context.params?.id}`,
Expand All @@ -229,6 +264,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => {

return {
props: {
// Parse/stringify to make sure all dates are strings and remove all undefined values
ownUser: session ? JSON.parse(JSON.stringify(session.user)) : null,
user: JSON.parse(JSON.stringify(userResponse.data.data)),
...(context.locale
Expand Down
14 changes: 6 additions & 8 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ model User {
created DateTime @default(now())
// The Meal Plan that the user owns
ownMealPlan MealPlan? @relation("owner")
// Any followed meal plans
followedMealPlans MealPlan[] @relation("followedMealPlans")
mealPlan MealPlan? @relation("owner")
}

model Follows {
follower User @relation("follower", fields: [followerId], references: [id], onDelete: Cascade)
followerId String
// The person that is following someone
follower User @relation("follower", fields: [followerId], references: [id], onDelete: Cascade)
followerId String
// The person that is being followed
following User @relation("following", fields: [followingId], references: [id], onDelete: Cascade)
followingId String
Expand Down Expand Up @@ -275,8 +275,6 @@ model MealPlan {
public Boolean @default(true)
followers User[] @relation("followedMealPlans")
recipes MealPlanRecipe[]
updated DateTime @updatedAt
Expand Down
Loading

0 comments on commit 99fff87

Please sign in to comment.