Skip to content

Commit

Permalink
Add addLike action, modularize LikeButton, fix multiple likes from sa…
Browse files Browse the repository at this point in the history
…me user
  • Loading branch information
T1LT committed Jan 16, 2024
1 parent 81e0581 commit b21de1e
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 17 deletions.
44 changes: 43 additions & 1 deletion app/(recipes)/actions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
"use server";

import z from "zod";
import { db, recipesTable, genRecipeId, usersTable } from "@/app/db";
import {
db,
recipesTable,
genRecipeId,
usersTable,
likesTable,
genLikeId,
} from "@/app/db";
import { auth } from "@/app/auth";
import { redirect } from "next/navigation";
import { newRecipeRateLimit } from "@/lib/rate-limit";
import { put } from "@vercel/blob";
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";

const MAX_FILE_SIZE = 400000;
const ACCEPTED_IMAGE_TYPES = [
Expand Down Expand Up @@ -133,3 +142,36 @@ export async function submitRecipe(

redirect(`/recipes/${id.replace(/^recipe_/, "")}`);
}

export async function addLike(recipeId: string, likes: number) {
const session = await auth();

if (!session?.user?.id) redirect("/login");

const userId = session.user.id;

try {
const id = genLikeId();

// add entry to likes table
await db
.insert(likesTable)
.values({ id, recipe_id: recipeId, user_id: userId });

// update recipe likes column
await db
.update(recipesTable)
.set({ likes: likes + 1 })
.where(eq(recipesTable.id, recipeId));
} catch (err) {
console.error(err);
return {
error: {
code: "INTERNAL_ERROR",
message: "Failed to like recipe. Please try again later.",
},
};
}

revalidatePath(`/recipes/${recipeId.replace(/^recipe_/, "")}`);
}
9 changes: 3 additions & 6 deletions app/(recipes)/recipes/[item]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { db, recipesTable } from "@/app/db";
import { sql } from "drizzle-orm";
import { Heart } from "lucide-react";
import { nanoid } from "nanoid";
import { headers } from "next/headers";
import Image from "next/image";
import { notFound } from "next/navigation";
import LikeButton from "../like-button";

async function getRecipe(id: string) {
const recipeId = `recipe_${id}`;
Expand Down Expand Up @@ -45,7 +45,7 @@ export default async function RecipeItem({
/>
)}
</div>
<div className="flex justify-between">
<div className="flex justify-between items-center">
<div className="flex flex-col space-y-2">
<p className="flex gap-1">
<span className="font-bold">Cuisine:</span>
Expand All @@ -60,10 +60,7 @@ export default async function RecipeItem({
{recipe.prepTime} min
</p>
</div>
<p className="flex items-center gap-1">
<Heart className="h-6 w-6" />
{recipe.likes}
</p>
<LikeButton likes={recipe.likes} recipeId={recipe.id} />
</div>
<div>
<h3 className="font-bold text-xl">Ingredients</h3>
Expand Down
30 changes: 30 additions & 0 deletions app/(recipes)/recipes/like-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import { useOptimistic } from "react";
import { addLike } from "../actions";
import { Heart } from "lucide-react";

interface LikeButtonProps {
likes: number;
recipeId: string;
}

export default function LikeButton({ likes, recipeId }: LikeButtonProps) {
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(state, _) => state + 1,
);

return (
<div className="flex items-center space-x-2 h-max">
<Heart
className="h-6 w-6 cursor-pointer"
onClick={async () => {
addOptimisticLike(1);
await addLike(recipeId, optimisticLikes);
}}
/>
<span className="text-lg">{Number(optimisticLikes)}</span>
</div>
);
}
27 changes: 17 additions & 10 deletions app/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
integer,
varchar,
timestamp,
unique,
} from "drizzle-orm/pg-core";
import { customAlphabet } from "nanoid";
import { nolookalikes } from "nanoid-dictionary";
Expand Down Expand Up @@ -83,16 +84,22 @@ export const genRecipeId = () => {
return `recipe_${nanoid(12)}`;
};

export const likesTable = pgTable("likes", {
id: varchar("id", { length: 256 }).primaryKey().notNull(),
recipe_id: varchar("recipe_id", { length: 256 })
.notNull()
.references(() => recipesTable.id),
user_id: varchar("user_id", { length: 256 })
.notNull()
.references(() => usersTable.id),
});
export const likesTable = pgTable(
"likes",
{
id: varchar("id", { length: 256 }).primaryKey().notNull(),
recipe_id: varchar("recipe_id", { length: 256 })
.notNull()
.references(() => recipesTable.id),
user_id: varchar("user_id", { length: 256 })
.notNull()
.references(() => usersTable.id),
},
(t) => ({
unq: unique().on(t.recipe_id, t.user_id),
}),
);

export const genLikeId = () => {
return `comment_${nanoid(12)}`;
return `like_${nanoid(12)}`;
};

0 comments on commit b21de1e

Please sign in to comment.