Skip to content

Commit

Permalink
Feat: Update 'LikeButton' to show loading state
Browse files Browse the repository at this point in the history
Now the Like button will have its loading state and spinner.
This loading spinner will be shown in two situations, at first render, i.e
when the likes are being fetch, and when the user likes a project, i.e if
the user likes it, it will render the spinner until the request its finish and
showing the updated number of likes or an error message if the request fails.

This will certaintly enhance the user experience.
  • Loading branch information
ITurres committed Mar 5, 2024
1 parent 397d916 commit e48e55d
Showing 1 changed file with 51 additions and 3 deletions.
54 changes: 51 additions & 3 deletions src/components/UI/LikeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import React, {
} from 'react';

import { SlLike } from 'react-icons/sl';
import { VscLoading } from 'react-icons/vsc';

import '../../styles/UI/LikeButton.scss';

import involvement from '../../services/involvementAPI/involvementAPI.ts';

/* eslint-disable no-undef */
interface LikeButtonProps {
itemId: string;
}
Expand All @@ -20,6 +20,8 @@ const LikeButton: React.FC<LikeButtonProps> = ({ itemId }) => {
const [wasLiked, setWasLiked] = useState(false);
const [likeCount, setLikeCount] = useState(0);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(true);
const maximumLoadingTime = 5000;

const likeBtn = useRef<HTMLButtonElement>(null);

Expand All @@ -35,6 +37,15 @@ const LikeButton: React.FC<LikeButtonProps> = ({ itemId }) => {
}
}, [wasLiked, styleButton]);

// ! terminate the loading state after a certain time.
// ? this is to prevent the loading state from being stuck.
// * and show the user the like button again.
const setLoadingToFalse = useCallback(() => {
setTimeout(() => {
setLoading(false);
}, maximumLoadingTime);
}, []);

interface Like {
// * disable camelcase since 'item_id' is the name of the property in the API response.
// eslint-disable-next-line camelcase
Expand All @@ -45,12 +56,21 @@ const LikeButton: React.FC<LikeButtonProps> = ({ itemId }) => {
const updateLikeCount = useCallback(async () => {
const likes: Like[] = (await involvement.getLikes()) as Like[];

if (likes.length === 0) {
setError(true);
// ? at this point the loading spinner is still showing.
// * so terminate the loading state, to show the error message.
setLoadingToFalse();
return;
}

const likedProject = likes.find((like) => like.item_id === itemId);

if (likedProject) {
setLikeCount(likedProject.likes);
setLoadingToFalse();
}
}, [itemId]);
}, [itemId, setLoadingToFalse]);

useEffect(() => {
updateLikeCount();
Expand All @@ -64,26 +84,54 @@ const LikeButton: React.FC<LikeButtonProps> = ({ itemId }) => {
setLikeCount((count) => count + 1);
}

if (error) {
// ? at this point, there was an error already, so user will attempt to like again.
// * so trigger the loading state again.
setLoading(true);
// * after some time, terminate the loading state, to show like button again.
setLoadingToFalse();
return;
}

const likePosted = await involvement.postLike(itemId);

if (likePosted === 'error') {
setError(true);
return;
}

setLoading(false);
// * after posting a like, update the like count.
updateLikeCount();
};

if (loading) {
return (
<button
type="button"
className="like-button text-hue-rotate"
ref={likeBtn}
onClick={handleLikeSubmit}
aria-label="loading"
>
<VscLoading size={20} className="like-icon loading-icon" />
</button>
);
}

return (
<button
type="button"
className="like-button text-hue-rotate"
ref={likeBtn}
onClick={handleLikeSubmit}
aria-label={error ? 'error' : 'like this project'}
>
<SlLike size={20} className="like-icon" />

{likeCount > 0 && (
{/* ? when the API likes ary comes empty, likeCount will be 0 */}
{/* * so show the error message that was set at the likes.length conditional */}
{(likeCount > 0 || error) && (
<span className="like-button__text">
&nbsp;
{!error ? likeCount : 'Error'}
Expand Down

0 comments on commit e48e55d

Please sign in to comment.