add user context and likes

This commit is contained in:
Aaron William Po
2023-02-08 07:43:59 -05:00
parent 20000cc4af
commit f6880deeb6
12 changed files with 300 additions and 47 deletions

View File

@@ -1,20 +1,84 @@
import Link from 'next/link';
import formatDistanceStrict from 'date-fns/formatDistanceStrict';
import format from 'date-fns/format';
import { useEffect, useState } from 'react';
import { useContext, useEffect, useState } from 'react';
import { FaRegThumbsUp, FaThumbsUp } from 'react-icons/fa';
import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import UserContext from '@/pages/contexts/userContext';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod';
const BeerInfoHeader: React.FC<{ beerPost: BeerPostQueryResult }> = ({ beerPost }) => {
const createdAtDate = new Date(beerPost.createdAt);
const [timeDistance, setTimeDistance] = useState('');
const { user } = useContext(UserContext);
useEffect(() => {
setTimeDistance(formatDistanceStrict(new Date(beerPost.createdAt), new Date()));
}, [beerPost.createdAt]);
const [loading, setLoading] = useState(true);
const [isLiked, setIsLiked] = useState(false);
useEffect(() => {
if (!user) {
setLoading(false);
return;
}
fetch(`/api/beers/${beerPost.id}/like/is-liked`)
.then((response) => response.json())
.then((data) => {
const parsed = APIResponseValidationSchema.safeParse(data);
if (!parsed.success) {
throw new Error('Invalid API response.');
}
const { payload } = parsed.data;
const parsedPayload = z
.object({
isLiked: z.boolean(),
})
.safeParse(payload);
if (!parsedPayload.success) {
throw new Error('Invalid API response payload.');
}
const { isLiked: alreadyLiked } = parsedPayload.data;
setIsLiked(alreadyLiked);
setLoading(false);
});
}, [user, beerPost.id]);
const handleLike = async () => {
const response = await fetch(`/api/beers/${beerPost.id}/like`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: '',
});
if (!response.ok) {
throw new Error('Something went wrong.');
}
const data = await response.json();
const parsed = APIResponseValidationSchema.safeParse(data);
if (!parsed.success) {
throw new Error('Invalid API response.');
}
const { success, message } = parsed.data;
setIsLiked(!isLiked);
console.log({ success, message });
};
return (
<div className="card flex flex-col justify-center bg-base-300">
<div className="card-body">
@@ -59,27 +123,30 @@ const BeerInfoHeader: React.FC<{ beerPost: BeerPostQueryResult }> = ({ beerPost
</div>
</div>
<div className="card-actions">
<button
type="button"
className={`btn gap-2 rounded-2xl ${
!isLiked ? 'btn-ghost outline' : 'btn-primary'
}`}
onClick={() => {
setIsLiked(!isLiked);
}}
>
{isLiked ? (
<>
<FaThumbsUp className="text-2xl" />
<span>Liked</span>
</>
) : (
<>
<FaRegThumbsUp className="text-2xl" />
<span>Like</span>
</>
)}
</button>
{user && (
<button
type="button"
className={`btn gap-2 rounded-2xl ${
!isLiked ? 'btn-ghost outline' : 'btn-primary'
}`}
onClick={() => {
handleLike();
}}
disabled={loading}
>
{isLiked ? (
<>
<FaThumbsUp className="text-2xl" />
<span>Liked</span>
</>
) : (
<>
<FaRegThumbsUp className="text-2xl" />
<span>Like</span>
</>
)}
</button>
)}
</div>
</div>
</div>

View File

@@ -7,6 +7,7 @@ interface FormTextAreaProps {
error: boolean;
id: string;
rows: number;
disabled?: boolean;
}
/**
@@ -17,6 +18,7 @@ interface FormTextAreaProps {
* error={true}
* placeholder="Test"
* rows={5}
* disabled
* />;
*
* @param props
@@ -25,6 +27,7 @@ interface FormTextAreaProps {
* @param props.error Whether or not the textarea has an error.
* @param props.id The id of the textarea.
* @param props.rows The number of rows to display in the textarea.
* @param props.disabled Whether or not the textarea is disabled.
*/
const FormTextArea: FunctionComponent<FormTextAreaProps> = ({
placeholder = '',
@@ -32,6 +35,7 @@ const FormTextArea: FunctionComponent<FormTextAreaProps> = ({
error,
id,
rows,
disabled = false,
}) => (
<textarea
id={id}
@@ -41,6 +45,7 @@ const FormTextArea: FunctionComponent<FormTextAreaProps> = ({
}`}
{...formValidationSchema}
rows={rows}
disabled={disabled}
/>
);

View File

@@ -10,6 +10,7 @@ interface FormInputProps {
type: 'email' | 'password' | 'text' | 'date';
id: string;
height?: string;
disabled?: boolean;
}
/**
@@ -20,6 +21,7 @@ interface FormInputProps {
* error={!!errors.name}
* type="text"
* id="name"
* disabled
* />;
*
* @param param0 The props for the FormTextInput component
@@ -30,6 +32,7 @@ interface FormInputProps {
* @param param0.type The input type (email, password, text, date).
* @param param0.id The id of the input.
* @param param0.height The height of the input.
* @param param0.disabled Whether or not the input is disabled.
*/
const FormTextInput: FunctionComponent<FormInputProps> = ({
placeholder = '',
@@ -37,6 +40,7 @@ const FormTextInput: FunctionComponent<FormInputProps> = ({
error,
type,
id,
disabled = false,
}) => (
<input
id={id}
@@ -46,6 +50,7 @@ const FormTextInput: FunctionComponent<FormInputProps> = ({
error ? 'input-error' : ''
}`}
{...formValidationSchema}
disabled={disabled}
/>
);