Skip to content

Commit

Permalink
Merge pull request #13 from ITurres/feature/like-button-loading-state
Browse files Browse the repository at this point in the history
Issue #9 🎫: LikeButton (UI component) loading state
  • Loading branch information
ITurres authored Mar 7, 2024
2 parents 397d916 + c93c071 commit df361b4
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 3 deletions.
57 changes: 54 additions & 3 deletions src/components/UI/LikeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import React, {
} from 'react';

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

import '../../styles/UI/LikeButton.scss';
import '../../styles/animations/loading-icon.scss';

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

/* eslint-disable no-undef */
interface LikeButtonProps {
itemId: string;
}
Expand All @@ -20,6 +21,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 +38,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 +57,23 @@ 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);
// ? at this point the loading spinner is still showing because of the initial API request.
// * so terminate the loading state, to show the like button with the like count.
setLoadingToFalse();
}
}, [itemId]);
}, [itemId, setLoadingToFalse]);

useEffect(() => {
updateLikeCount();
Expand All @@ -64,26 +87,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
16 changes: 16 additions & 0 deletions src/styles/animations/loading-icon.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.loading-icon {
animation: spin 1s linear infinite;
-webkit-animation: spin 1s linear infinite; // * Safari and Chrome.
-moz-animation: spin 1s linear infinite; // * Firefox.
-o-animation: spin 1s linear infinite; // * Opera.

@keyframes spin {
from {
transform: rotate(0deg);
}

to {
transform: rotate(360deg);
}
}
}

0 comments on commit df361b4

Please sign in to comment.