main
/**
* 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<typeof issues.data> } | 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<typeof useBoardData>