/** * Data loading for one issue board. Four independent requests so a single * failing endpoint cannot sink the page: the issue page drives the content, * while labels / milestones / members only feed filters and pickers and fall * back to "加载失败" inside their own menus. */ import { type BoardRef, getBoard, type IssueQuery, listIssues, listLabels, listMilestones, listSpaceMembers, } from '@app/domains/issues/model' import { boardKey, spaceMembersPageKey, useAsync } from '@app/lib' import { useRef } from 'react' /** Page size for the issue list; the board's cap is 100 per request. */ export const ISSUE_PAGE_SIZE = 50 export function useBoardData(ref: BoardRef, query: IssueQuery, reloadKey: number) { const queryKey = JSON.stringify(query) // eslint-disable-next-line react-hooks/exhaustive-deps const issues = useAsync(() => listIssues(ref, query), [ref.space, ref.board, queryKey, reloadKey], { cacheKey: boardKey(ref.space, ref.board, `issues:${queryKey}`), }) /** * The cache key carries the filters, so every filter change is a *cold* key * and `useAsync` would drop the rows it was showing — one tick of the filter * panel painted a skeleton. Keep this board's previous page on screen while * the next one loads; a board switch still starts from a clean slate. */ const lastPage = useRef<{ board: string; page: NonNullable } | null>(null) if (issues.data) lastPage.current = { board: ref.board, page: issues.data } const keepLast = !issues.data && !issues.error && lastPage.current?.board === ref.board const issuesState = keepLast ? { ...issues, data: lastPage.current!.page, refreshing: true } : issues // eslint-disable-next-line react-hooks/exhaustive-deps const board = useAsync(() => getBoard(ref), [ref.space, ref.board, reloadKey], { cacheKey: boardKey(ref.space, ref.board, 'board'), }) // eslint-disable-next-line react-hooks/exhaustive-deps const labels = useAsync(() => listLabels(ref), [ref.space, ref.board, reloadKey], { cacheKey: boardKey(ref.space, ref.board, 'labels'), }) // eslint-disable-next-line react-hooks/exhaustive-deps const milestones = useAsync(() => listMilestones(ref), [ref.space, ref.board, reloadKey], { cacheKey: boardKey(ref.space, ref.board, 'milestones'), }) // eslint-disable-next-line react-hooks/exhaustive-deps const members = useAsync(() => listSpaceMembers(ref.space), [ref.space, reloadKey], { cacheKey: spaceMembersPageKey(ref.space), }) return { board, issues: issuesState, labels, milestones, members } } export type BoardData = ReturnType