refactor(skill)!: add file node view-state flow and mode-based file data hook

- introduce resolving/ready/missing node view-state to avoid unsupported flicker

- switch useSkillFileData to explicit mode: none/content/download

- add hook tests for view-state transitions and mode query gating

BREAKING CHANGE: useSkillFileData signature changed from (appId, nodeId, isEditable) to (appId, nodeId, mode).
This commit is contained in:
yyh
2026-02-06 15:34:27 +08:00
parent f1100b82f9
commit dc213ca76c
5 changed files with 334 additions and 9 deletions

View File

@ -0,0 +1,73 @@
import { useRef } from 'react'
export type FileNodeViewState = 'resolving' | 'ready' | 'missing'
type ResolveFileNodeViewStateParams = {
hasFileTabId: boolean
hasCurrentFileNode: boolean
isNodeMapLoading: boolean
isNodeMapFetching: boolean
isNodeMapFetched: boolean
isNodeResolutionPending: boolean
}
type UseFileNodeViewStateParams = {
fileTabId: string | null
hasCurrentFileNode: boolean
isNodeMapLoading: boolean
isNodeMapFetching: boolean
isNodeMapFetched: boolean
}
export const resolveFileNodeViewState = ({
hasFileTabId,
hasCurrentFileNode,
isNodeMapLoading,
isNodeMapFetching,
isNodeMapFetched,
isNodeResolutionPending,
}: ResolveFileNodeViewStateParams): FileNodeViewState => {
if (!hasFileTabId || hasCurrentFileNode)
return 'ready'
if (isNodeResolutionPending && (!isNodeMapFetched || isNodeMapLoading || isNodeMapFetching))
return 'resolving'
return 'missing'
}
export function useFileNodeViewState({
fileTabId,
hasCurrentFileNode,
isNodeMapLoading,
isNodeMapFetching,
isNodeMapFetched,
}: UseFileNodeViewStateParams): FileNodeViewState {
const hasFileTabId = Boolean(fileTabId)
const resolutionRef = useRef<{ tabId: string | null, pending: boolean }>({
tabId: fileTabId,
pending: hasFileTabId,
})
if (resolutionRef.current.tabId !== fileTabId) {
resolutionRef.current = {
tabId: fileTabId,
pending: hasFileTabId,
}
}
if (fileTabId && resolutionRef.current.pending) {
const isQuerySettled = isNodeMapFetched && !isNodeMapLoading && !isNodeMapFetching
if (hasCurrentFileNode || isQuerySettled)
resolutionRef.current.pending = false
}
return resolveFileNodeViewState({
hasFileTabId,
hasCurrentFileNode,
isNodeMapLoading,
isNodeMapFetching,
isNodeMapFetched,
isNodeResolutionPending: resolutionRef.current.pending,
})
}