Added star functionality to Recipe page.
This commit is contained in:
		
							parent
							
								
									d52ab9d97e
								
							
						
					
					
						commit
						4ea3c86522
					
				
							
								
								
									
										25
									
								
								src/api/addStar.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/api/addStar.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| import { ApiError } from './ApiError' | ||||
| import ExpiredTokenError from './ExpiredTokenError' | ||||
| 
 | ||||
| export interface AddStarDeps { | ||||
|     token: string | ||||
|     username: string | ||||
|     slug: string | ||||
| } | ||||
| 
 | ||||
| const addStar = async ({ slug, token, username }: AddStarDeps): Promise<void> => { | ||||
|     const headers = new Headers() | ||||
|     headers.set('Authorization', `Bearer ${token}`) | ||||
|     const response = await fetch(import.meta.env.VITE_MME_API_URL + `/recipes/${username}/${slug}/star`, { | ||||
|         headers, | ||||
|         method: 'POST', | ||||
|         mode: 'cors' | ||||
|     }) | ||||
|     if (response.status === 401) { | ||||
|         throw new ExpiredTokenError() | ||||
|     } else if (!response.ok) { | ||||
|         throw new ApiError(response.status, response.statusText) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export default addStar | ||||
							
								
								
									
										25
									
								
								src/api/removeStar.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/api/removeStar.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| import { ApiError } from './ApiError' | ||||
| import ExpiredTokenError from './ExpiredTokenError' | ||||
| 
 | ||||
| export interface RemoveStarDeps { | ||||
|     token: string | ||||
|     username: string | ||||
|     slug: string | ||||
| } | ||||
| 
 | ||||
| const removeStar = async ({ token, username, slug }: RemoveStarDeps) => { | ||||
|     const headers = new Headers() | ||||
|     headers.set('Authorization', `Bearer ${token}`) | ||||
|     const response = await fetch(import.meta.env.VITE_MME_API_URL + `/recipes/${username}/${slug}/star`, { | ||||
|         headers, | ||||
|         method: 'DELETE', | ||||
|         mode: 'cors' | ||||
|     }) | ||||
|     if (response.status === 401) { | ||||
|         throw new ExpiredTokenError() | ||||
|     } else if (!response.ok) { | ||||
|         throw new ApiError(response.status, response.statusText) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export default removeStar | ||||
| @ -13,6 +13,7 @@ export interface RawFullRecipeView { | ||||
|     text: string | ||||
|     owner: UserInfoView | ||||
|     starCount: number | ||||
|     isStarred: boolean | null | ||||
|     viewerCount: number | ||||
|     mainImage: RawImageView | ||||
|     isPublic: boolean | ||||
| @ -30,6 +31,7 @@ interface FullRecipeView { | ||||
|     text: string | ||||
|     owner: UserInfoView | ||||
|     starCount: number | ||||
|     isStarred: boolean | null | ||||
|     viewerCount: number | ||||
|     mainImage: ImageView | ||||
|     isPublic: boolean | ||||
| @ -47,6 +49,7 @@ export const toFullRecipeView = ({ | ||||
|     text, | ||||
|     owner, | ||||
|     starCount, | ||||
|     isStarred, | ||||
|     viewerCount, | ||||
|     mainImage: rawMainImage, | ||||
|     isPublic | ||||
| @ -62,6 +65,7 @@ export const toFullRecipeView = ({ | ||||
|     text, | ||||
|     owner, | ||||
|     starCount, | ||||
|     isStarred, | ||||
|     viewerCount, | ||||
|     mainImage: toImageView(rawMainImage), | ||||
|     isPublic | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||||
| import { QueryObserverSuccessResult, useQuery, useQueryClient } from '@tanstack/react-query' | ||||
| import { QueryObserverSuccessResult, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' | ||||
| import { ApiError } from '../../api/ApiError' | ||||
| import getImage from '../../api/getImage' | ||||
| import getRecipe from '../../api/getRecipe' | ||||
| @ -8,6 +8,8 @@ import { useAuth } from '../../auth' | ||||
| import RecipeVisibilityIcon from '../../components/recipe-visibility-icon/RecipeVisibilityIcon' | ||||
| import UserIconAndName from '../../components/user-icon-and-name/UserIconAndName' | ||||
| import classes from './recipe.module.css' | ||||
| import addStar from '../../api/addStar' | ||||
| import removeStar from '../../api/removeStar' | ||||
| 
 | ||||
| export interface RecipeProps { | ||||
|     username: string | ||||
| @ -20,7 +22,7 @@ const Recipe = ({ username, slug }: RecipeProps) => { | ||||
| 
 | ||||
|     const recipeQuery = useQuery( | ||||
|         { | ||||
|             queryKey: ['recipe', username, slug], | ||||
|             queryKey: ['recipes', username, slug], | ||||
|             queryFn: ({ signal: abortSignal }) => | ||||
|                 getRecipe({ | ||||
|                     abortSignal, | ||||
| @ -46,6 +48,50 @@ const Recipe = ({ username, slug }: RecipeProps) => { | ||||
|         queryClient | ||||
|     ) | ||||
| 
 | ||||
|     const addStarMutation = useMutation({ | ||||
|         mutationFn: () => { | ||||
|             if (authContext.token !== null) { | ||||
|                 return addStar({ | ||||
|                     token: authContext.token, | ||||
|                     slug, | ||||
|                     username | ||||
|                 }) | ||||
|             } else { | ||||
|                 return Promise.resolve() | ||||
|             } | ||||
|         }, | ||||
|         onSuccess: () => { | ||||
|             queryClient.invalidateQueries({ queryKey: ['recipes', username, slug] }) | ||||
|         } | ||||
|     }) | ||||
| 
 | ||||
|     const removeStarMutation = useMutation({ | ||||
|         mutationFn: () => { | ||||
|             if (authContext.token !== null) { | ||||
|                 return removeStar({ | ||||
|                     token: authContext.token, | ||||
|                     slug, | ||||
|                     username | ||||
|                 }) | ||||
|             } else { | ||||
|                 return Promise.resolve() | ||||
|             } | ||||
|         }, | ||||
|         onSuccess: () => { | ||||
|             queryClient.invalidateQueries({ queryKey: ['recipes', username, slug] }) | ||||
|         } | ||||
|     }) | ||||
| 
 | ||||
|     const onStarButtonClick = () => { | ||||
|         if (recipeQuery.isSuccess) { | ||||
|             if (recipeQuery.data.isStarred) { | ||||
|                 removeStarMutation.mutate() | ||||
|             } else { | ||||
|                 addStarMutation.mutate() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (recipeQuery.isLoading || mainImageQuery.isLoading) { | ||||
|         return 'Loading...' | ||||
|     } else if (recipeQuery.isError) { | ||||
| @ -73,9 +119,9 @@ const Recipe = ({ username, slug }: RecipeProps) => { | ||||
|                 <div className={classes.info}> | ||||
|                     <div className={classes.infoRow}> | ||||
|                         <h1 className={classes.recipeTitle}>{recipe.title}</h1> | ||||
|                         <button className={classes.starButton}> | ||||
|                         <button className={classes.starButton} onClick={onStarButtonClick}> | ||||
|                             <FontAwesomeIcon icon="star" className={classes.star} size="sm" /> | ||||
|                             <span></span> | ||||
|                             <span>{recipe.isStarred ? 'Starred' : 'Star'}</span> | ||||
|                             {recipe.starCount} | ||||
|                         </button> | ||||
|                     </div> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Jesse Brault
						Jesse Brault