Basic FullRecipeView and Recipe page.

This commit is contained in:
Jesse Brault 2024-07-31 11:15:02 -05:00
parent 5815ea5879
commit fa7d104bd2
8 changed files with 139 additions and 27 deletions

View File

@ -1,5 +1,7 @@
import { QueryClient } from '@tanstack/react-query'
import { AuthContextType } from './auth'
export default interface RouterContext {
auth: AuthContextType
queryClient: QueryClient
}

53
src/api/getRecipe.ts Normal file
View File

@ -0,0 +1,53 @@
import { ApiError } from './ApiError'
import FullRecipeView, { RawFullRecipeView } from './types/FullRecipeView'
import { toImageView } from './types/ImageView'
const getRecipe = async (
token: string | null,
ownerUsername: string,
slug: string
): Promise<FullRecipeView> => {
const headers = new Headers()
if (token !== null) {
headers.set('Authorization', `Bearer ${token}`)
}
const response = await fetch(
import.meta.env.VITE_MME_API_URL + `/recipes/${ownerUsername}/${slug}`,
{
headers,
mode: 'cors'
}
)
if (response.ok) {
const {
id,
created: rawCreated,
modified: rawModified,
slug,
title,
text,
ownerId,
ownerUsername,
starCount,
viewerCount,
mainImage: rawMainImage
} = (await response.json()) as RawFullRecipeView
return {
id,
created: new Date(rawCreated),
modified: rawModified ? new Date(rawModified) : null,
slug,
title,
text,
ownerId,
ownerUsername,
starCount,
viewerCount,
mainImage: toImageView(rawMainImage)
}
} else {
throw new ApiError(response.status, response.statusText)
}
}
export default getRecipe

View File

@ -1,4 +1,5 @@
import { ApiError } from './ApiError'
import { toImageView } from './types/ImageView'
import RecipeInfosView, { RawRecipeInfosView } from './types/RecipeInfosView'
const getRecipeInfos = async (
@ -43,19 +44,7 @@ const getRecipeInfos = async (
ownerUsername,
isPublic,
starCount,
mainImage: {
url: rawMainImage.url,
created: new Date(rawMainImage.created),
modified: rawMainImage.modified
? new Date(rawMainImage.modified)
: null,
filename: rawMainImage.fileName,
mimeType: rawMainImage.mimeType,
alt: rawMainImage.alt,
caption: rawMainImage.caption,
owner: rawMainImage.owner,
isPublic: rawMainImage.isPublic
},
mainImage: toImageView(rawMainImage),
slug
})
)

View File

@ -0,0 +1,31 @@
import ImageView, { RawImageView } from './ImageView'
export interface RawFullRecipeView {
id: number
created: string
modified: string | null
slug: string
title: string
text: string
ownerId: number
ownerUsername: string
starCount: number
viewerCount: number
mainImage: RawImageView
}
interface FullRecipeView {
id: number
created: Date
modified: Date | null
slug: string
title: string
text: string
ownerId: number
ownerUsername: string
starCount: number
viewerCount: number
mainImage: ImageView
}
export default FullRecipeView

View File

@ -4,7 +4,7 @@ export interface RawImageView {
url: string
created: string
modified: string | null
fileName: string
filename: string
mimeType: string
alt: string | null
caption: string | null
@ -24,4 +24,26 @@ interface ImageView {
isPublic: boolean
}
export const toImageView = ({
url,
created: rawCreated,
modified: rawModified,
filename,
mimeType,
alt,
caption,
owner,
isPublic
}: RawImageView): ImageView => ({
url,
created: new Date(rawCreated),
modified: rawModified ? new Date(rawModified) : null,
filename,
mimeType,
alt,
caption,
owner,
isPublic
})
export default ImageView

View File

@ -10,17 +10,18 @@ import { fas } from '@fortawesome/free-solid-svg-icons'
// Font-Awesome: load icons
library.add(fas)
// Create queryClient
const queryClient = new QueryClient()
// Create router
const router = createRouter({
context: {
auth: undefined!
auth: undefined!,
queryClient
},
routeTree
})
// Create queryClient
const queryClient = new QueryClient()
// Register the router instance for type safety
declare module '@tanstack/react-router' {
interface Register {

View File

@ -1,13 +1,15 @@
import { Route } from '../../routes/recipes_/$username.$slug'
import FullRecipeView from '../../api/types/FullRecipeView'
export interface RecipeProps {}
export interface RecipeProps {
recipe: FullRecipeView
}
const Recipe = ({}: RecipeProps) => {
const { username, slug } = Route.useParams()
const Recipe = ({ recipe }: RecipeProps) => {
return (
<>
Hello, {username}/{slug}
</>
<article>
<h1>{recipe.title}</h1>
<div dangerouslySetInnerHTML={{ __html: recipe.text }} />
</article>
)
}

View File

@ -1,6 +1,18 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute, useLoaderData } from '@tanstack/react-router'
import getRecipe from '../../api/getRecipe'
import Recipe from '../../pages/recipe/Recipe'
export const Route = createFileRoute('/recipes/$username/$slug')({
component: Recipe
loader: ({ context, params }) =>
context.queryClient.ensureQueryData({
queryKey: ['recipe', params.username, params.slug],
queryFn: () =>
getRecipe(context.auth.token, params.username, params.slug)
}),
component: () => {
const recipe = useLoaderData({
from: '/recipes/$username/$slug'
})
return <Recipe {...{ recipe }} />
}
})