修掉导出面板卡在「正在读取导出信息…」
- `usePanel` 的注册表开关改成「进过小红书 tab」或「当前目标就是小红书」两个条件取或:从 tab 条切过来的路径原本既不开开关、也不发生一次文件访问,面板于是永远等不到第一次读取; - 两条入口(点目标大卡、点 tab 条)合并到同一个 `enterTarget`,不再各写一份; - `visited` 单独留着,是为了离开 tab 之后仍保有开关,表单自动保存的 600ms 窗口不被切 tab 掐掉; - 新增 `tests/xhsMinitoolPanelTargets.test.tsx`,三条入口各钉一条。
This commit is contained in:
@@ -39,21 +39,36 @@ export function usePanel({
|
||||
*/
|
||||
const [workspaceEntered, setWorkspaceEntered] = useState(false);
|
||||
/**
|
||||
* 导出注册表的开关比订阅更窄:只有真的点进过小红书 tab 才打开。进陶泥儿不该在用户项目里
|
||||
* 导出注册表的开关比订阅更窄:只有真的用过小红书 tab 才打开。进陶泥儿不该在用户项目里
|
||||
* 补出一份 `.export/xhs-minitool.json`。
|
||||
*
|
||||
* 判据是**两个条件取或**,缺一不可:
|
||||
* - `visited` 让「离开小红书 tab 之后」继续保有开关(表单自动保存的 600ms 窗口不该因为切
|
||||
* tab 被掐掉);
|
||||
* - `activeTarget` 让「从别的 tab 切过来」当场打开开关。只认 `visited` 是不够的——`visited`
|
||||
* 只在点目标大卡(`openTarget`)时置真,而从 tab 条切过来走的是 `selectTarget`,于是面板
|
||||
* 会永远停在「正在读取导出信息…」:一次文件访问都没发生。
|
||||
*/
|
||||
const [xiaohongshuEntered, setXiaohongshuEntered] = useState(false);
|
||||
const [xiaohongshuVisited, setXiaohongshuVisited] = useState(false);
|
||||
const xiaohongshuEntered =
|
||||
xiaohongshuVisited || activeTarget === Target.Xiaohongshu;
|
||||
|
||||
const openTarget = useCallback((target: Target) => {
|
||||
/** 切到某个目标时的共同动作:两条入口(点卡片、点 tab 条)都必须经过它。 */
|
||||
const enterTarget = useCallback((target: Target) => {
|
||||
setActiveTarget(target);
|
||||
setStage(Stage.Workspace);
|
||||
setWorkspaceEntered(true);
|
||||
if (target === Target.Xiaohongshu) setXiaohongshuEntered(true);
|
||||
if (target === Target.Xiaohongshu) setXiaohongshuVisited(true);
|
||||
}, []);
|
||||
|
||||
const selectTarget = useCallback((target: Target) => {
|
||||
setActiveTarget(target);
|
||||
}, []);
|
||||
const openTarget = useCallback(
|
||||
(target: Target) => {
|
||||
enterTarget(target);
|
||||
setStage(Stage.Workspace);
|
||||
setWorkspaceEntered(true);
|
||||
},
|
||||
[enterTarget],
|
||||
);
|
||||
|
||||
const selectTarget = enterTarget;
|
||||
|
||||
const taonier: TaonierTabState = useTaonierTab({
|
||||
enabled: workspaceEntered && Boolean(projectPath),
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// @vitest-environment jsdom
|
||||
/**
|
||||
* 面板目标切换与「导出注册表开关」的接线。
|
||||
*
|
||||
* 回归的是一次真实故障:点目标大卡进来会打开注册表开关,从顶部 tab 条切过来却不会——那时
|
||||
* `useXhsMinitoolExport` 一次文件访问都没发生,`form` 永远是 `null`,左半面板于是永久停在
|
||||
* 「正在读取导出信息…」上,看起来像卡死。这个开关只该由「当前目标 / 曾经到过」决定,
|
||||
* 不该由「从哪条入口进来」决定。
|
||||
*/
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { Target } from '../src/view/project-development/export/model';
|
||||
|
||||
const { xiaohongshuInputs } = vi.hoisted(() => ({
|
||||
xiaohongshuInputs: [] as { enabled: boolean; minitoolEnabled: boolean }[],
|
||||
}));
|
||||
|
||||
vi.mock(
|
||||
'../src/view/project-development/export/state/useXiaohongshuTab',
|
||||
() => ({
|
||||
useXiaohongshuTab: (input: {
|
||||
enabled: boolean;
|
||||
minitoolEnabled: boolean;
|
||||
}) => {
|
||||
xiaohongshuInputs.push(input);
|
||||
return {} as never;
|
||||
},
|
||||
}),
|
||||
);
|
||||
vi.mock('../src/view/project-development/export/state/useTaonierTab', () => ({
|
||||
useTaonierTab: () => ({}),
|
||||
}));
|
||||
|
||||
const { usePanel } = await import(
|
||||
'../src/view/project-development/export/state/usePanel'
|
||||
);
|
||||
|
||||
function renderPanel() {
|
||||
return renderHook(() =>
|
||||
usePanel({
|
||||
projectPath: '/tmp/project',
|
||||
ensureConversationReadAllowed: async () => true,
|
||||
ensureConversationWriteAllowed: async () => true,
|
||||
onClose: () => {},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function lastMinitoolEnabled(): boolean | undefined {
|
||||
return xiaohongshuInputs.at(-1)?.minitoolEnabled;
|
||||
}
|
||||
|
||||
describe('导出面板的目标切换', () => {
|
||||
it('从目标大卡直接进小红书:注册表开关打开', () => {
|
||||
xiaohongshuInputs.length = 0;
|
||||
const panel = renderPanel();
|
||||
|
||||
act(() => panel.result.current.openTarget(Target.Xiaohongshu));
|
||||
|
||||
expect(lastMinitoolEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('先进陶泥儿再从 tab 条切到小红书:注册表开关一样要打开', () => {
|
||||
xiaohongshuInputs.length = 0;
|
||||
const panel = renderPanel();
|
||||
|
||||
act(() => panel.result.current.openTarget(Target.Taonier));
|
||||
// 进陶泥儿不该在用户项目里补出一份 `.export/xhs-minitool.json`。
|
||||
expect(lastMinitoolEnabled()).toBe(false);
|
||||
|
||||
act(() => panel.result.current.selectTarget(Target.Xiaohongshu));
|
||||
|
||||
expect(lastMinitoolEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('到过小红书之后再切走:开关保持打开,表单自动保存的窗口不被掐掉', () => {
|
||||
xiaohongshuInputs.length = 0;
|
||||
const panel = renderPanel();
|
||||
|
||||
act(() => panel.result.current.openTarget(Target.Xiaohongshu));
|
||||
act(() => panel.result.current.selectTarget(Target.Taonier));
|
||||
|
||||
expect(lastMinitoolEnabled()).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user