fix(FN-XXX): keep experimental views off by default
This commit is contained in:
5
.changeset/fix-experimental-defaults.md
Normal file
5
.changeset/fix-experimental-defaults.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Keep experimental dashboard views off by default until project settings enable them.
|
||||
@@ -354,6 +354,7 @@ function AppInner() {
|
||||
taskStuckTimeoutMs,
|
||||
showQuickChatFAB,
|
||||
prAuthAvailable,
|
||||
settingsLoaded,
|
||||
experimentalFeatures,
|
||||
insightsEnabled,
|
||||
roadmapEnabled,
|
||||
@@ -371,9 +372,11 @@ function AppInner() {
|
||||
const agentsEnabled = true;
|
||||
|
||||
// Redirect to board if feature-gated views are disabled.
|
||||
// Only run after settings have been loaded (experimentalFeatures is non-empty)
|
||||
useEffect(() => {
|
||||
if (Object.keys(experimentalFeatures).length === 0) return;
|
||||
if (!settingsLoaded) return;
|
||||
if (taskView === "skills" && !skillsEnabled) {
|
||||
handleChangeTaskView("board");
|
||||
}
|
||||
if (taskView === "insights" && !insightsEnabled) {
|
||||
handleChangeTaskView("board");
|
||||
}
|
||||
@@ -392,7 +395,7 @@ function AppInner() {
|
||||
if (taskView === "todos" && !todosEnabled) {
|
||||
handleChangeTaskView("board");
|
||||
}
|
||||
}, [taskView, insightsEnabled, roadmapEnabled, experimentalFeatures, handleChangeTaskView, agentsEnabled, memoryEnabled, devServerEnabled, todosEnabled]);
|
||||
}, [taskView, settingsLoaded, skillsEnabled, insightsEnabled, roadmapEnabled, handleChangeTaskView, agentsEnabled, memoryEnabled, devServerEnabled, todosEnabled]);
|
||||
|
||||
// Auto-close nodes overlay if feature flag is toggled off while overlay is open
|
||||
useEffect(() => {
|
||||
@@ -595,9 +598,7 @@ function AppInner() {
|
||||
|
||||
// Project view
|
||||
if (taskView === "skills") {
|
||||
if (!skillsEnabled) {
|
||||
// Redirect to board if skills view is not enabled
|
||||
handleChangeTaskView("board");
|
||||
if (!settingsLoaded || !skillsEnabled) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
@@ -636,6 +637,9 @@ function AppInner() {
|
||||
}
|
||||
|
||||
if (taskView === "roadmaps") {
|
||||
if (!settingsLoaded || !roadmapEnabled) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<PageErrorBoundary>
|
||||
<Suspense fallback={null}>
|
||||
@@ -698,6 +702,9 @@ function AppInner() {
|
||||
}
|
||||
|
||||
if (taskView === "insights") {
|
||||
if (!settingsLoaded || !insightsEnabled) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<PageErrorBoundary>
|
||||
<Suspense fallback={null}>
|
||||
@@ -712,6 +719,9 @@ function AppInner() {
|
||||
}
|
||||
|
||||
if (taskView === "memory") {
|
||||
if (!settingsLoaded || !memoryEnabled) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<PageErrorBoundary>
|
||||
<Suspense fallback={null}>
|
||||
@@ -722,6 +732,9 @@ function AppInner() {
|
||||
}
|
||||
|
||||
if (taskView === "todos") {
|
||||
if (!settingsLoaded || !todosEnabled) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<PageErrorBoundary>
|
||||
<Suspense fallback={null}>
|
||||
@@ -732,6 +745,9 @@ function AppInner() {
|
||||
}
|
||||
|
||||
if (taskView === "devserver" || taskView === "dev-server") {
|
||||
if (!settingsLoaded || !devServerEnabled) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<PageErrorBoundary>
|
||||
<Suspense fallback={null}>
|
||||
|
||||
@@ -1664,6 +1664,39 @@ describe("App view switching", () => {
|
||||
expect(screen.queryByTestId("view-overflow-insights")).toBeNull();
|
||||
});
|
||||
|
||||
it("keeps experimental views off until settings load and falls back to board when no flag is enabled", async () => {
|
||||
localStorage.setItem(taskViewStorageKey(), "insights");
|
||||
|
||||
let resolveSettings: ((settings: Settings) => void) | undefined;
|
||||
vi.mocked(fetchSettings).mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
resolveSettings = resolve as (settings: Settings) => void;
|
||||
}),
|
||||
);
|
||||
|
||||
render(<App />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTitle("Board view")).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(document.querySelector(".insights-view")).toBeNull();
|
||||
expect(document.querySelector(".board")).toBeNull();
|
||||
|
||||
resolveSettings?.({
|
||||
...defaultSettings,
|
||||
experimentalFeatures: {},
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(".board")).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(document.querySelector(".insights-view")).toBeNull();
|
||||
localStorage.removeItem(taskViewStorageKey());
|
||||
});
|
||||
|
||||
it("does not render memory view button when memoryView experimental feature is disabled", async () => {
|
||||
// Keep another overflow item enabled so the overflow trigger still renders.
|
||||
(fetchSettings as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
||||
|
||||
@@ -44,6 +44,7 @@ describe("useAppSettings", () => {
|
||||
expect(result.current.globalPaused).toBe(true);
|
||||
expect(result.current.enginePaused).toBe(false);
|
||||
expect(result.current.prAuthAvailable).toBe(true);
|
||||
expect(result.current.settingsLoaded).toBe(true);
|
||||
expect(result.current.taskStuckTimeoutMs).toBe(600000);
|
||||
expect(result.current.showQuickChatFAB).toBe(false);
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface UseAppSettingsResult {
|
||||
taskStuckTimeoutMs: number | undefined;
|
||||
showQuickChatFAB: boolean;
|
||||
prAuthAvailable: boolean;
|
||||
settingsLoaded: boolean;
|
||||
experimentalFeatures: Record<string, boolean>;
|
||||
insightsEnabled: boolean;
|
||||
roadmapEnabled: boolean;
|
||||
@@ -39,6 +40,7 @@ export function useAppSettings(projectId?: string): UseAppSettingsResult {
|
||||
const [taskStuckTimeoutMs, setTaskStuckTimeoutMs] = useState<number | undefined>(undefined);
|
||||
const [showQuickChatFAB, setShowQuickChatFAB] = useState(false);
|
||||
const [prAuthAvailable, setPrAuthAvailable] = useState(false);
|
||||
const [settingsLoaded, setSettingsLoaded] = useState(false);
|
||||
const [experimentalFeatures, setExperimentalFeatures] = useState<Record<string, boolean>>({});
|
||||
const [insightsEnabled, setInsightsEnabled] = useState(false);
|
||||
const [roadmapEnabled, setRoadmapEnabled] = useState(false);
|
||||
@@ -77,9 +79,18 @@ export function useAppSettings(projectId?: string): UseAppSettingsResult {
|
||||
setDevServerEnabled(features.devServerView === true || features.devServer === true);
|
||||
setTodosEnabled(features.todoView === true);
|
||||
}
|
||||
|
||||
setSettingsLoaded(true);
|
||||
}, [projectId]);
|
||||
|
||||
useEffect(() => {
|
||||
setSettingsLoaded(false);
|
||||
setExperimentalFeatures({});
|
||||
setInsightsEnabled(false);
|
||||
setRoadmapEnabled(false);
|
||||
setMemoryEnabled(false);
|
||||
setDevServerEnabled(false);
|
||||
setTodosEnabled(false);
|
||||
void refresh();
|
||||
}, [refresh]);
|
||||
|
||||
@@ -142,6 +153,7 @@ export function useAppSettings(projectId?: string): UseAppSettingsResult {
|
||||
taskStuckTimeoutMs,
|
||||
showQuickChatFAB,
|
||||
prAuthAvailable,
|
||||
settingsLoaded,
|
||||
experimentalFeatures,
|
||||
insightsEnabled,
|
||||
roadmapEnabled,
|
||||
|
||||
Reference in New Issue
Block a user