146 lines
4.7 KiB
TypeScript
146 lines
4.7 KiB
TypeScript
import { root } from '@repo/core'
|
||
import { type ListTemplatesInput, type ListTemplatesResult, TemplateController, type TemplateDetail } from '@repo/sdk'
|
||
import { useCallback, useRef, useState } from 'react'
|
||
|
||
import { type ApiError } from '@/lib/types'
|
||
|
||
import { OWNER_ID } from '@/lib/auth'
|
||
import { handleError } from './use-error'
|
||
|
||
type ListCategoryTemplatesParams = Partial<Omit<ListTemplatesInput, 'ownerId'>>
|
||
|
||
interface UseCategoryTemplatesOptions {
|
||
categoryId?: string
|
||
limit?: number
|
||
sortBy?: 'createdAt' | 'updatedAt'
|
||
sortOrder?: 'asc' | 'desc'
|
||
}
|
||
|
||
const DEFAULT_PARAMS = {
|
||
limit: 20,
|
||
sortBy: 'createdAt' as const,
|
||
sortOrder: 'desc' as const,
|
||
}
|
||
|
||
export const useCategoryTemplates = (
|
||
categoryIdOrOptions?: string | UseCategoryTemplatesOptions,
|
||
additionalOptions?: Omit<UseCategoryTemplatesOptions, 'categoryId'>
|
||
) => {
|
||
// 支持多种调用方式:
|
||
// useCategoryTemplates('category-id')
|
||
// useCategoryTemplates('category-id', { limit: 50 })
|
||
// useCategoryTemplates({ categoryId: 'category-id', limit: 50 })
|
||
const options: UseCategoryTemplatesOptions | undefined = typeof categoryIdOrOptions === 'string'
|
||
? { categoryId: categoryIdOrOptions, ...additionalOptions }
|
||
: categoryIdOrOptions
|
||
|
||
const [loading, setLoading] = useState(false)
|
||
const [loadingMore, setLoadingMore] = useState(false)
|
||
const [error, setError] = useState<ApiError | null>(null)
|
||
const [data, setData] = useState<ListTemplatesResult>()
|
||
const currentPageRef = useRef(1)
|
||
const hasMoreRef = useRef(true)
|
||
const currentCategoryIdRef = useRef<string | undefined>(options?.categoryId)
|
||
|
||
const execute = useCallback(async (params?: ListCategoryTemplatesParams) => {
|
||
setLoading(true)
|
||
setError(null)
|
||
currentPageRef.current = params?.page || 1
|
||
|
||
// 确定要使用的 categoryId:params > options(来自 props)> currentCategoryIdRef
|
||
const categoryIdToUse = params?.categoryId ?? options?.categoryId ?? currentCategoryIdRef.current
|
||
|
||
// 更新当前 categoryId ref
|
||
if (categoryIdToUse !== undefined) {
|
||
currentCategoryIdRef.current = categoryIdToUse
|
||
}
|
||
|
||
const template = root.get(TemplateController)
|
||
const requestParams: ListTemplatesInput = {
|
||
...DEFAULT_PARAMS,
|
||
...options,
|
||
...params,
|
||
page: params?.page ?? 1,
|
||
categoryId: categoryIdToUse,
|
||
ownerId: OWNER_ID,
|
||
}
|
||
|
||
const { data, error } = await handleError(
|
||
async () => await template.list(requestParams),
|
||
)
|
||
|
||
if (error) {
|
||
setError(error)
|
||
setLoading(false)
|
||
return { data: undefined, error }
|
||
}
|
||
|
||
const templates = data?.templates || []
|
||
const currentPage = requestParams.page || 1
|
||
const totalPages = data?.totalPages || 1
|
||
hasMoreRef.current = currentPage < totalPages
|
||
setData(data)
|
||
setLoading(false)
|
||
return { data, error: null }
|
||
}, [options])
|
||
|
||
const loadMore = useCallback(async () => {
|
||
if (loadingMore || loading || !hasMoreRef.current) return { data: undefined, error: null }
|
||
|
||
setLoadingMore(true)
|
||
const nextPage = currentPageRef.current + 1
|
||
|
||
// 使用最新的 categoryId:options(来自 props)> currentCategoryIdRef
|
||
const categoryIdToUse = options?.categoryId ?? currentCategoryIdRef.current
|
||
|
||
const template = root.get(TemplateController)
|
||
const requestParams: ListTemplatesInput = {
|
||
...DEFAULT_PARAMS,
|
||
...options,
|
||
page: nextPage,
|
||
categoryId: categoryIdToUse,
|
||
ownerId: OWNER_ID,
|
||
}
|
||
|
||
const { data: newData, error } = await handleError(
|
||
async () => await template.list(requestParams),
|
||
)
|
||
|
||
if (error) {
|
||
setLoadingMore(false)
|
||
return { data: undefined, error }
|
||
}
|
||
|
||
const newTemplates = newData?.templates || []
|
||
const totalPages = newData?.totalPages || 1
|
||
hasMoreRef.current = nextPage < totalPages
|
||
currentPageRef.current = nextPage
|
||
|
||
setData((prev) => ({
|
||
...newData,
|
||
templates: [...(prev?.templates || []), ...newTemplates],
|
||
}))
|
||
setLoadingMore(false)
|
||
return { data: newData, error: null }
|
||
}, [loading, loadingMore, options])
|
||
|
||
const refetch = useCallback(() => {
|
||
hasMoreRef.current = true
|
||
return execute()
|
||
}, [execute])
|
||
|
||
return {
|
||
data,
|
||
templates: data?.templates || [],
|
||
loading,
|
||
loadingMore,
|
||
error,
|
||
execute,
|
||
refetch,
|
||
loadMore,
|
||
hasMore: hasMoreRef.current,
|
||
}
|
||
}
|
||
|
||
export type { TemplateDetail }
|