Skip to content

Commit

Permalink
like button
Browse files Browse the repository at this point in the history
  • Loading branch information
GraemeFulton committed Jun 13, 2024
1 parent eba06a2 commit c825630
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 22 deletions.
250 changes: 232 additions & 18 deletions components/LikeButton.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,240 @@
import React, { useState } from 'react';
import axios from 'axios';
import React, { useCallback, useEffect, useState } from "react";
import axios from "axios";
import { debounce } from "lodash";

const LikeButton = ({ post }) => {
const [liked, setLiked] = useState(false);
import { getLikeCount } from "@/lib/api";
import toast from "react-hot-toast";

const handleLike = async () => {
try {
// Make API call to update the post's like field
// await axios.put(`/api/posts/${postId}`, { liked: !liked });
const LikeButton = ({ post, user }) => {
const [userLikeObject, setUserLikeObject] = useState(null);

// Toggle the liked state
setLiked(!liked);
} catch (error) {
console.error('Error updating post like:', error);
const [likeCount, setLikeCount] = useState(null);

const refetchLikeCount = async (post, user) => {
const lc = await getLikeCount(post?.id);
if (lc?.post?.data?.attributes?.likeCount) {
setLikeCount(lc?.post?.data?.attributes?.likeCount);
}
if (lc?.post?.data?.attributes?.likes?.data) {
const likeObject = lc?.post?.data?.attributes?.likes?.data?.find(
like => like?.attributes?.user?.data?.id == user.id
);
if (likeObject) {
setUserLikeObject(likeObject);
const { like, love, fire, unicorn } = likeObject.attributes;
setReactions({
like,
love,
fire,
unicorn,
});
} else {
setUserLikeObject(null);
setReactions({
like: false,
love: false,
fire: false,
unicorn: false,
});
}
}
// setLikeCount(likeCount);
};

const [reactions, setReactions] = useState({
like: false,
love: false,
fire: false,
unicorn: false,
});

const [creatingLike, setCreatingLike] = useState(false);

useEffect(() => {
setLikeCount(post.attributes.likeCount);

if (user?.id) {
const likeObject = post.attributes?.likes?.data?.find(
like => like?.attributes?.user?.data?.id == user.id
);
if (likeObject) {
setUserLikeObject(likeObject);
const { like, love, fire, unicorn } = likeObject.attributes;
setReactions({
like,
love,
fire,
unicorn,
});
} else {
setUserLikeObject(null);
setReactions({
like: false,
love: false,
fire: false,
unicorn: false,
});
}
}
}, [user, post]);

const debounceSave = useCallback(
debounce(async (post, newReactions, total, userLikeObject, user, type) => {
if (!user) {
return false;
}
if (type == "update") {
// Your axios call here, using newReactions
let entry = {
total: total,
...newReactions,
// slug: slug, //slug is always the same when editing a draft - so we don't need to update it
};

let updatePostEndpointConfig = {
method: "put",
url: `${process.env.NEXT_PUBLIC_API_URL}/api/likes/${userLikeObject.id}`,
headers: {
Authorization: `Bearer ${user?.jwt}`,
},
data: {
data: {
...entry,
},
},
};

const updateData = await axios(updatePostEndpointConfig);
console.log("saved");
refetchLikeCount(post, user);
} else if (type == "create") {
setCreatingLike(true);
//create a new like object
let entry = {
total: total,
...newReactions,
user: user?.id,
post: post?.id,
};

let updatePostEndpointConfig = {
method: "post",
url: `${process.env.NEXT_PUBLIC_API_URL}/api/likes`,
headers: {
Authorization: `Bearer ${user?.jwt}`,
},
data: {
data: {
...entry,
},
},
};

const publishData = await axios(updatePostEndpointConfig);
setUserLikeObject(publishData.data.data);
setCreatingLike(false);
console.log("created");
refetchLikeCount();
}
}, 2000),
[getLikeCount]
);

const handleReaction = async reaction => {

if(!user){
toast("Sign in to react to posts.", {
duration: 5000,
icon: '💔',
});

return false
}

const addLike = reactions[reaction] ? false : true;

const newReactions = { ...reactions, [reaction]: addLike };
if (reactions) {
setReactions({ ...newReactions });
}
let newLikeCountObj = { ...likeCount };
newLikeCountObj[reaction] = addLike
? newLikeCountObj[reaction] + 1
: newLikeCountObj[reaction] - 1;
const totalLikes = Object.values(newReactions).reduce(
(total, reaction) => total + (reaction ? 1 : 0),
0
);
newLikeCountObj.total = totalLikes;
setLikeCount(newLikeCountObj);

try {
//for each reaction, if it is true, add 1 to a total
let total = 0;
for (const key in newReactions) {
if (newReactions[key]) {
total++;
}
};
}

return (
<button onClick={handleLike}>
{liked ? '❤️ Liked' : '🤍 Like'}
if (userLikeObject) {
debounceSave(post, newReactions, total, userLikeObject, user, "update");
} else {
const newReactions = { ...reactions, [reaction]: addLike };
if (reactions) {
setReactions({ ...newReactions });
}
debounceSave(post, newReactions, total, userLikeObject, user, "create");
}

// Toggle the liked state
} catch (error) {
console.log(error);
alert("error saving like");
console.error("Error updating post like:", error);
}
};

return (
<div className="flex flex-col gap-2 mt-4">
<p className={`${likeCount?.total>0?'h-6':'opacity-0 h-0'} text-gray-600 tracking-tight font-semibold text-center transition transition-all duration-400`}>❤️ {likeCount?.total}</p>
<div className="rounded-full h-fit w-fit p-2.5 flex flex-col gap-2 bg-gray-100/90">
<button
disabled={creatingLike}
onClick={() => handleReaction("like")}
className={`${reactions.like ? "shadow-lg bg-white" : "bg-white/40"} group rounded-full p-1 h-12 w-14 flex flex-col justify-center`}
>
<div
className={`${reactions.like ? "opacity-100 text-[26px] drop-shadow-lg" : "text-[22px] opacity-80 group-hover:opacity-100"} transition transition-all duration-400 mx-auto flex gap-2`}
>
😍 {likeCount?.like>0?<div className={`${reactions.like?'text-gray-700':'text-gray-500'} text-sm my-auto`}>{likeCount?.like}</div>:null}
</div>
</button>
);
<button
disabled={creatingLike}
onClick={() => handleReaction("unicorn")}
className={`${reactions.unicorn ? "shadow-lg bg-white" : "bg-white/40"} group rounded-full p-1 h-12 w-14 flex flex-col justify-center`}
>
<div
className={`${reactions.unicorn ? "opacity-100 text-[26px] drop-shadow-lg" : "text-[22px] opacity-80 group-hover:opacity-100"} transition transition-all duration-400 mx-auto flex gap-2`}
>
🦄 {likeCount?.unicorn>0?<div className={`${reactions.unicorn?'text-gray-700':'text-gray-500'} text-sm my-auto`}>{likeCount?.unicorn}</div>:null}
</div>
</button>
<button
disabled={creatingLike}
onClick={() => handleReaction("fire")}
className={`${reactions.fire ? "shadow-lg bg-white" : "bg-white/40"} group rounded-full p-1 h-12 w-14 flex flex-col justify-center`}
>
<div
className={`${reactions.fire ? "opacity-100 text-[26px] drop-shadow-lg" : "text-[22px] opacity-80 group-hover:opacity-100"} transition transition-all duration-400 mx-auto flex gap-2`}
>
🔥 {likeCount?.fire>0?<div className={`${reactions.fire?'text-gray-700':'text-gray-500'} text-sm my-auto`}>{likeCount?.fire}</div>:null}
</div>
</button>
</div>
</div>
);
};

export default LikeButton;
export default LikeButton;
14 changes: 14 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import allProductsQuery from "./queries/allProductsQuery";
import { addBase64s } from "./utils/blurHashToDataURL";
import creatorArticlesQuery from "./queries/creatorArticlesQuery";
import getUserRelatedPostsFromPostId from "./queries/getUserRelatedPostsFromPostId";
import likeCountQuery from "./queries/likeCountQuery";

/**
* main fetch
Expand Down Expand Up @@ -586,6 +587,19 @@ export async function getNewsAndMoreNews(slug, preview, type) {
return data;
}

export async function getLikeCount(id, preview) {
let data = await fetchAPI(likeCountQuery, {
preview,
variables: {
id: id,
},
});

//make function to check all arrays and objects for blurhash
data = addBase64s(data)

return data;
}
export async function getTool(slug, preview) {
let data = await fetchAPI(singleToolQuery, {
preview,
Expand Down
34 changes: 34 additions & 0 deletions lib/queries/likeCountQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export default `
query LikeCount($id: ID!) {
post(id:$id) {
data {
id
attributes{
likeCount{
total
like
fire
unicorn
}
likes{
data{
id
attributes{
like
love
fire
unicorn
total
user{
data{
id
}
}
}
}
}
}
}
}
}
`;
23 changes: 23 additions & 0 deletions lib/queries/singleToolQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,29 @@ export default `
#tagline
date
updatedAt
likeCount{
total
like
fire
unicorn
}
likes{
data{
id
attributes{
like
love
fire
unicorn
total
user{
data{
id
}
}
}
}
}
seo{
metaDesc
canonical
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"iron-session": "8.0.1",
"js-cookie": "^2.2.1",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"metascraper": "^5.33.8",
"metascraper-amazon": "^5.33.7",
"metascraper-author": "^5.33.7",
Expand Down
4 changes: 2 additions & 2 deletions pages/toolbox/[slug].js
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ const ToolContent = ({
{/* Content under header */}
<Container maxWidth="w-full z-10">
<div className="grid grid-cols-3 lg:grid-cols-12 gap-3 xl:gap-7 max-w-[1320px] mx-auto md:px-0 h-full">
<div className="hidden sticky top-0 h-fit lg:col-span-1 lg:block">
<LikeButton post={post}/>
<div className="hidden sticky top-6 h-fit lg:col-span-1 lg:block">
<LikeButton post={post} user={user}/>
</div>
<div className="col-span-3 border border-gray-300/60 rounded-2xl overflow-hidden lg:col-span-8 flex flex-col gap-3 bg-white">
<div className="grid gap-3 md:px-0 -mb-4">
Expand Down

0 comments on commit c825630

Please sign in to comment.