Working on Recipe star count/star ability.
This commit is contained in:
parent
5fb88eef0a
commit
d52ab9d97e
@ -1,6 +1,6 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { Link } from '@tanstack/react-router'
|
import { Link } from '@tanstack/react-router'
|
||||||
import RecipeVisibilityIcon from '../recipe-visibility-icon/RecipeVisibilityIcon'
|
import RecipeVisibilityIcon from '../recipe-visibility-icon/RecipeVisibilityIcon'
|
||||||
import StarCount from '../star-count/StarCount'
|
|
||||||
import UserIconAndName from '../user-icon-and-name/UserIconAndName'
|
import UserIconAndName from '../user-icon-and-name/UserIconAndName'
|
||||||
import classes from './recipe-card.module.css'
|
import classes from './recipe-card.module.css'
|
||||||
|
|
||||||
@ -46,7 +46,10 @@ const RecipeCard = ({
|
|||||||
>
|
>
|
||||||
<h1 className={classes.title}>{title}</h1>
|
<h1 className={classes.title}>{title}</h1>
|
||||||
</Link>
|
</Link>
|
||||||
<StarCount count={starCount} />
|
<span className={classes.starInfo}>
|
||||||
|
<FontAwesomeIcon icon="star" className={classes.star} size="sm" />
|
||||||
|
{starCount}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.infoRow}>
|
<div className={classes.infoRow}>
|
||||||
<UserIconAndName username={ownerUsername} />
|
<UserIconAndName username={ownerUsername} />
|
||||||
|
@ -23,3 +23,13 @@
|
|||||||
margin-block: 0;
|
margin-block: 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.star-info {
|
||||||
|
display: flex;
|
||||||
|
column-gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star {
|
||||||
|
color: var(--primary-yellow);
|
||||||
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
||||||
import classes from './star-count.module.css'
|
|
||||||
|
|
||||||
export interface StarCountProps {
|
|
||||||
count: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const StarCount = ({ count }: StarCountProps) => (
|
|
||||||
<span className={classes.starInfo}>
|
|
||||||
<FontAwesomeIcon icon="star" className={classes.star} size="sm" />
|
|
||||||
{count}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default StarCount
|
|
@ -1,9 +0,0 @@
|
|||||||
.star-info {
|
|
||||||
display: flex;
|
|
||||||
column-gap: 5px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.star {
|
|
||||||
color: var(--primary-yellow);
|
|
||||||
}
|
|
@ -1,29 +1,90 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import { QueryObserverSuccessResult, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||||
|
import { ApiError } from '../../api/ApiError'
|
||||||
|
import getImage from '../../api/getImage'
|
||||||
|
import getRecipe from '../../api/getRecipe'
|
||||||
import FullRecipeView from '../../api/types/FullRecipeView'
|
import FullRecipeView from '../../api/types/FullRecipeView'
|
||||||
|
import { useAuth } from '../../auth'
|
||||||
import RecipeVisibilityIcon from '../../components/recipe-visibility-icon/RecipeVisibilityIcon'
|
import RecipeVisibilityIcon from '../../components/recipe-visibility-icon/RecipeVisibilityIcon'
|
||||||
import StarCount from '../../components/star-count/StarCount'
|
|
||||||
import UserIconAndName from '../../components/user-icon-and-name/UserIconAndName'
|
import UserIconAndName from '../../components/user-icon-and-name/UserIconAndName'
|
||||||
import classes from './recipe.module.css'
|
import classes from './recipe.module.css'
|
||||||
|
|
||||||
export interface RecipeProps {
|
export interface RecipeProps {
|
||||||
recipe: FullRecipeView
|
username: string
|
||||||
imgUrl: string
|
slug: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const Recipe = ({ recipe, imgUrl }: RecipeProps) => {
|
const Recipe = ({ username, slug }: RecipeProps) => {
|
||||||
|
const authContext = useAuth()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
const recipeQuery = useQuery(
|
||||||
|
{
|
||||||
|
queryKey: ['recipe', username, slug],
|
||||||
|
queryFn: ({ signal: abortSignal }) =>
|
||||||
|
getRecipe({
|
||||||
|
abortSignal,
|
||||||
|
authContext,
|
||||||
|
username,
|
||||||
|
slug
|
||||||
|
})
|
||||||
|
},
|
||||||
|
queryClient
|
||||||
|
)
|
||||||
|
|
||||||
|
const mainImageQuery = useQuery(
|
||||||
|
{
|
||||||
|
enabled: recipeQuery.isSuccess,
|
||||||
|
queryKey: ['images', recipeQuery.data?.mainImage.owner.username, recipeQuery.data?.mainImage.filename],
|
||||||
|
queryFn: ({ signal }) =>
|
||||||
|
getImage({
|
||||||
|
accessToken: authContext.token,
|
||||||
|
signal,
|
||||||
|
url: recipeQuery.data!.mainImage.url
|
||||||
|
})
|
||||||
|
},
|
||||||
|
queryClient
|
||||||
|
)
|
||||||
|
|
||||||
|
if (recipeQuery.isLoading || mainImageQuery.isLoading) {
|
||||||
|
return 'Loading...'
|
||||||
|
} else if (recipeQuery.isError) {
|
||||||
|
const { error } = recipeQuery
|
||||||
|
if (error instanceof ApiError) {
|
||||||
|
if (error.status === 404) {
|
||||||
|
return 'No such recipe.'
|
||||||
|
} else {
|
||||||
|
return `ApiError: ${error.status} ${error.message}`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return `Error: ${error.name} ${error.message}`
|
||||||
|
}
|
||||||
|
} else if (mainImageQuery.isError) {
|
||||||
|
const { error } = mainImageQuery
|
||||||
|
return `Error: ${error.name} ${error.message}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: recipe } = recipeQuery as QueryObserverSuccessResult<FullRecipeView>
|
||||||
|
const { data: mainImageUrl } = mainImageQuery as QueryObserverSuccessResult<string>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.fullRecipeContainer}>
|
<div className={classes.fullRecipeContainer}>
|
||||||
<article className={classes.fullRecipe}>
|
<article className={classes.fullRecipe}>
|
||||||
<div className={classes.info}>
|
<div className={classes.info}>
|
||||||
<div className={classes.infoRow}>
|
<div className={classes.infoRow}>
|
||||||
<h1 className={classes.recipeTitle}>{recipe.title}</h1>
|
<h1 className={classes.recipeTitle}>{recipe.title}</h1>
|
||||||
<StarCount count={recipe.starCount} />
|
<button className={classes.starButton}>
|
||||||
|
<FontAwesomeIcon icon="star" className={classes.star} size="sm" />
|
||||||
|
<span></span>
|
||||||
|
{recipe.starCount}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.infoRow}>
|
<div className={classes.infoRow}>
|
||||||
<UserIconAndName username={recipe.owner.username} />
|
<UserIconAndName username={recipe.owner.username} />
|
||||||
<RecipeVisibilityIcon isPublic={recipe.isPublic} />
|
<RecipeVisibilityIcon isPublic={recipe.isPublic} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<img src={imgUrl} className={classes.mainImage} />
|
<img src={mainImageUrl} className={classes.mainImage} />
|
||||||
<div dangerouslySetInnerHTML={{ __html: recipe.text }} />
|
<div dangerouslySetInnerHTML={{ __html: recipe.text }} />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,3 +28,13 @@
|
|||||||
.main-image {
|
.main-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.star-button {
|
||||||
|
display: flex;
|
||||||
|
column-gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star {
|
||||||
|
color: var(--primary-yellow);
|
||||||
|
}
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
||||||
import { createFileRoute, useParams } from '@tanstack/react-router'
|
import { createFileRoute, useParams } from '@tanstack/react-router'
|
||||||
import { ApiError } from '../../api/ApiError'
|
|
||||||
import getImage from '../../api/getImage'
|
|
||||||
import getRecipe from '../../api/getRecipe'
|
|
||||||
import { useAuth } from '../../auth'
|
|
||||||
import Recipe from '../../pages/recipe/Recipe'
|
import Recipe from '../../pages/recipe/Recipe'
|
||||||
|
|
||||||
export const Route = createFileRoute('/recipes/$username/$slug')({
|
export const Route = createFileRoute('/recipes/$username/$slug')({
|
||||||
@ -11,63 +6,6 @@ export const Route = createFileRoute('/recipes/$username/$slug')({
|
|||||||
const { username, slug } = useParams({
|
const { username, slug } = useParams({
|
||||||
from: '/recipes/$username/$slug'
|
from: '/recipes/$username/$slug'
|
||||||
})
|
})
|
||||||
const authContext = useAuth()
|
return <Recipe {...{ username, slug }} />
|
||||||
const queryClient = useQueryClient()
|
|
||||||
const {
|
|
||||||
isLoading,
|
|
||||||
error,
|
|
||||||
data: recipe
|
|
||||||
} = useQuery(
|
|
||||||
{
|
|
||||||
queryKey: ['recipe', username, slug],
|
|
||||||
queryFn({ signal: abortSignal }) {
|
|
||||||
return getRecipe({
|
|
||||||
abortSignal,
|
|
||||||
authContext,
|
|
||||||
username,
|
|
||||||
slug
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
queryClient
|
|
||||||
)
|
|
||||||
|
|
||||||
const {
|
|
||||||
isLoading: isImageLoading,
|
|
||||||
error: imageError,
|
|
||||||
data: imgUrl
|
|
||||||
} = useQuery(
|
|
||||||
{
|
|
||||||
enabled: recipe !== undefined,
|
|
||||||
queryKey: ['images', recipe?.mainImage.owner.username, recipe?.mainImage.filename],
|
|
||||||
queryFn: ({ signal }) =>
|
|
||||||
getImage({
|
|
||||||
accessToken: authContext.token,
|
|
||||||
signal,
|
|
||||||
url: recipe!.mainImage.url
|
|
||||||
})
|
|
||||||
},
|
|
||||||
queryClient
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isLoading || isImageLoading) {
|
|
||||||
return 'Loading...'
|
|
||||||
} else if (error !== null) {
|
|
||||||
if (error instanceof ApiError) {
|
|
||||||
if (error.status === 404) {
|
|
||||||
return `No such recipe.`
|
|
||||||
} else {
|
|
||||||
return `ApiError: ${error.status} ${error.message}`
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return `Error: ${error.name} ${error.message}`
|
|
||||||
}
|
|
||||||
} else if (imageError !== null) {
|
|
||||||
return `Image loading error: ${imageError} ${imageError.message}`
|
|
||||||
} else if (recipe !== undefined && imgUrl !== undefined) {
|
|
||||||
return <Recipe {...{ recipe, imgUrl }} />
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user