feat: enhance endpoint responses with pagination, fixes #163

This commit is contained in:
Hossein Mehrabi 2023-09-01 15:27:12 +03:30
parent 642e5df20a
commit 2b7e6db146
No known key found for this signature in database
GPG Key ID: 45C04964191AFAA1
9 changed files with 76 additions and 37 deletions

View File

@ -5,6 +5,7 @@ import { Hero } from '../../components/Hero'
import { PostsGrid } from '../../components/PostsGrid'
import { uiConfigs } from '../../configs/ui.configs'
import { useRecentPosts } from '../../queries/useRecentPosts.query'
import { ApiPaginatedPayload } from '../../types/data.types'
import { LPE } from '../../types/lpe.types'
import { lsdUtils } from '../../utils/lsd.utils'
import { PodcastShowsPreview } from '../PodcastShowsPreview'
@ -16,14 +17,14 @@ export type HomePageProps = React.DetailedHTMLProps<
data: {
tags: string[]
shows: LPE.Podcast.Show[]
latest: LPE.Post.Document[]
latest: ApiPaginatedPayload<LPE.Post.Document[]>
highlighted: LPE.Post.Document[]
}
}
export const HomePage: React.FC<HomePageProps> = ({
data,
data: { highlighted = [], shows = [], tags = [], latest = [] },
data: { highlighted = [], shows = [], tags = [], latest },
...props
}) => {
const query = useRecentPosts({ initialData: latest, limit: 10 })

View File

@ -5,12 +5,13 @@ import PodcastShowCard from '@/components/Podcasts/PodcastShowCard'
import { Button, Typography } from '@acid-info/lsd-react'
import styled from '@emotion/styled'
import { useRecentEpisodes } from '../queries/useRecentEpisodes.query'
import { ApiPaginatedPayload } from '../types/data.types'
import { LPE } from '../types/lpe.types'
import { lsdUtils } from '../utils/lsd.utils'
interface Props {
show: LPE.Podcast.Show
latestEpisodes: LPE.Podcast.Document[]
latestEpisodes: ApiPaginatedPayload<LPE.Podcast.Document[]>
highlightedEpisodes: LPE.Podcast.Document[]
}

View File

@ -29,7 +29,7 @@ Page.getLayout = function getLayout(page: React.ReactNode) {
export const getStaticProps: GetStaticProps<PageProps> = async () => {
const { data: tags = [] } = await unbodyApi.getTopics(true)
const { data: highlighted } = await unbodyApi.getHighlightedPosts()
const { data: latest = [] } = await unbodyApi.getRecentPosts({
const { data: latest } = await unbodyApi.getRecentPosts({
skip: 0,
limit: 15,
})
@ -46,7 +46,10 @@ export const getStaticProps: GetStaticProps<PageProps> = async () => {
data: {
tags,
shows,
latest,
latest: {
data: latest?.data || [],
hasMore: latest?.hasMore,
},
highlighted,
},
},

View File

@ -5,12 +5,13 @@ import { GetStaticPropsContext } from 'next'
import { useRouter } from 'next/router'
import { ReactNode } from 'react'
import { DefaultLayout } from '../../../layouts/DefaultLayout'
import { ApiPaginatedPayload } from '../../../types/data.types'
import { LPE } from '../../../types/lpe.types'
import { getPostLink } from '../../../utils/route.utils'
interface PodcastShowProps {
show: LPE.Podcast.Show
latestEpisodes: LPE.Podcast.Document[]
latestEpisodes: ApiPaginatedPayload<LPE.Podcast.Document[]>
highlightedEpisodes: LPE.Podcast.Document[]
errors: string | null
}

View File

@ -1,21 +1,22 @@
import { useInfiniteQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { api } from '../services/api.service'
import { ApiPaginatedPayload } from '../types/data.types'
import { LPE } from '../types/lpe.types'
export const useRecentEpisodes = ({
initialData = [],
initialData = { data: [], hasMore: true },
limit = 10,
showSlug,
}: {
initialData?: LPE.Podcast.Document[]
initialData?: ApiPaginatedPayload<LPE.Podcast.Document[]>
showSlug: string
limit?: number
}) => {
const query = useInfiniteQuery(
['recent-episodes', showSlug, initialData, limit],
async ({ pageParam }) => {
const firstPageLimit = initialData.length
const firstPageLimit = initialData.data.length
const _limit = pageParam === 1 ? firstPageLimit : limit
const skip =
pageParam === 1 ? 0 : (pageParam - 2) * limit + firstPageLimit
@ -24,8 +25,8 @@ export const useRecentEpisodes = ({
.getLatestEpisodes({ skip, limit: _limit, showSlug })
.then((res) => ({
page: pageParam,
posts: res.data,
hasMore: res.data.length !== 0,
posts: res.data.data,
hasMore: res.data.hasMore,
}))
},
{
@ -34,8 +35,8 @@ export const useRecentEpisodes = ({
pages: [
{
page: 1,
hasMore: true,
posts: initialData,
posts: initialData.data,
hasMore: initialData?.hasMore,
},
],
},

View File

@ -1,27 +1,28 @@
import { useInfiniteQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { api } from '../services/api.service'
import { ApiPaginatedPayload } from '../types/data.types'
import { LPE } from '../types/lpe.types'
export const useRecentPosts = ({
initialData = [],
initialData,
limit = 10,
}: {
initialData?: LPE.Post.Document[]
initialData?: ApiPaginatedPayload<LPE.Post.Document[]>
limit?: number
}) => {
const query = useInfiniteQuery(
['latest-posts', initialData, limit],
async ({ pageParam }) => {
const firstPageLimit = initialData.length
const firstPageLimit = initialData?.data.length || 0
const _limit = pageParam === 1 ? firstPageLimit : limit
const skip =
pageParam === 1 ? 0 : (pageParam - 2) * limit + firstPageLimit
return api.getRecentPosts({ skip, limit: _limit }).then((res) => ({
page: pageParam,
posts: res.data,
hasMore: res.data.length !== 0,
posts: res.data.data,
hasMore: res.data.hasMore,
}))
},
{
@ -30,8 +31,8 @@ export const useRecentPosts = ({
pages: [
{
page: 1,
hasMore: true,
posts: initialData,
hasMore: initialData?.hasMore,
posts: initialData?.data || [],
},
],
},

View File

@ -1,4 +1,4 @@
import { ApiResponse } from '../types/data.types'
import { ApiPaginatedResponse, ApiResponse } from '../types/data.types'
import { LPE } from '../types/lpe.types'
export class ApiService {
@ -8,7 +8,7 @@ export class ApiService {
}: {
skip?: number
limit?: number
}): Promise<ApiResponse<LPE.Post.Document[]>> =>
}): Promise<ApiPaginatedResponse<LPE.Post.Document[]>> =>
fetch(`/api/posts?skip=${skip}&limit=${limit}`)
.then((res) => res.json())
.catch((e) => {
@ -24,7 +24,7 @@ export class ApiService {
skip?: number
limit?: number
showSlug: string
}): Promise<ApiResponse<LPE.Podcast.Document[]>> =>
}): Promise<ApiPaginatedResponse<LPE.Podcast.Document[]>> =>
fetch(`/api/podcasts/${showSlug}/episodes?skip=${skip}&limit=${limit}`)
.then((res) => res.json())
.catch((e) => {

View File

@ -13,7 +13,11 @@ import {
Txt2VecOpenAiGetObjectsTextBlockNearTextInpObj,
} from '../../lib/unbody/unbody.generated'
import { getWebhookData } from '../../pages/api/webhook'
import { ApiResponse, SearchResultItem } from '../../types/data.types'
import {
ApiPaginatedPayload,
ApiResponse,
SearchResultItem,
} from '../../types/data.types'
import { LPE } from '../../types/lpe.types'
import {
CreatePromiseResult,
@ -60,8 +64,10 @@ export class UnbodyService {
initialData: {
posts: LPE.Post.Document[]
articles: LPE.Article.Data[]
episodes: LPE.Podcast.Document[]
staticPages: LPE.StaticPage.Document[]
} = { posts: [], staticPages: [] }
} = { articles: [], episodes: [], posts: [], staticPages: [] }
initialDataPromise: CreatePromiseResult<any> = null as any
constructor(private apiKey: string, private projectId: string) {
@ -136,6 +142,8 @@ export class UnbodyService {
this.initialData = {
posts,
articles,
episodes,
staticPages,
}
callback(this.initialData)
@ -892,13 +900,22 @@ export class UnbodyService {
skip?: number
limit?: number
}) =>
this.getPodcastEpisodes({
skip,
limit,
showSlug,
highlighted: 'include',
populateShow: false,
})
this.handleRequest<ApiPaginatedPayload<LPE.Podcast.Document[]>>(
async () => {
await this.loadInitialData()
let episodes = [...this.initialData.episodes]
if (showSlug)
episodes = episodes.filter((ep) => ep.show?.slug === showSlug)
const data = episodes.slice(skip, skip + limit)
return {
data,
hasMore: episodes.length > skip + limit,
}
},
{ data: [], hasMore: false },
)
getRecentPosts = async ({
limit = 10,
@ -907,12 +924,23 @@ export class UnbodyService {
limit?: number
skip?: number
}) =>
this.handleRequest(async () => {
await this.loadInitialData()
const { posts } = this.initialData
this.handleRequest<ApiPaginatedPayload<LPE.Post.Document[]>>(
async () => {
await this.loadInitialData()
const { posts } = this.initialData
return posts.slice(skip, skip + limit)
}, [])
const data = posts.slice(skip, skip + limit)
return {
data,
hasMore: posts.length > skip + limit,
}
},
{
data: [],
hasMore: false,
},
)
getHighlightedPosts = async () =>
this.handleRequest<LPE.Post.Document[]>(async () => {

View File

@ -10,6 +10,9 @@ export type ApiResponse<T> = {
errors: any
}
export type ApiPaginatedPayload<T> = { data: T; hasMore: boolean }
export type ApiPaginatedResponse<T> = ApiResponse<ApiPaginatedPayload<T>>
export type SearchResultItem<T> = {
doc: T
score: number