feat(FN-002): enable browser back-button navigation within the SPA dashboard
Previously the back button left the dashboard page entirely because useNavigationHistory was only enabled for mobile viewports. Now it's enabled for all viewports — desktop and mobile — so the browser back button dismisses modals and reverts view changes within the SPA. Changes: - Enable useNavigationHistory for desktop (enabled: true instead of enabled: isMobile) - Remove all isMobile ? historyAwareHandler : plainHandler ternaries - Always use navigation-aware handlers (openDetailTask, openSettingsWithNav, etc.) - Update JSDoc to reflect desktop support
This commit is contained in:
5
.changeset/browser-back-button-spa-integration.md
Normal file
5
.changeset/browser-back-button-spa-integration.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Enable browser back-button navigation within the SPA dashboard. Previously, the back button would leave the dashboard entirely. Now it dismisses the top modal or reverts to the previous view, matching standard SPA behavior on both desktop and mobile.
|
||||||
@@ -227,8 +227,8 @@ function AppInner() {
|
|||||||
const viewportMode = useViewportMode();
|
const viewportMode = useViewportMode();
|
||||||
const isMobile = viewportMode === "mobile";
|
const isMobile = viewportMode === "mobile";
|
||||||
|
|
||||||
// Navigation history for mobile back button / iOS swipe-back.
|
// Navigation history for browser back button (desktop + mobile).
|
||||||
const { pushNav, replaceCurrent } = useNavigationHistory({ enabled: isMobile });
|
const { pushNav, replaceCurrent } = useNavigationHistory({ enabled: true });
|
||||||
|
|
||||||
// View state must be defined before useTasks since useTasks depends on taskView for SSE gating
|
// View state must be defined before useTasks since useTasks depends on taskView for SSE gating
|
||||||
const { viewMode, setViewMode, taskView, handleChangeTaskView } = useViewState({
|
const { viewMode, setViewMode, taskView, handleChangeTaskView } = useViewState({
|
||||||
@@ -245,7 +245,7 @@ function AppInner() {
|
|||||||
|
|
||||||
const { views: pluginDashboardViews } = usePluginDashboardViews(currentProject?.id);
|
const { views: pluginDashboardViews } = usePluginDashboardViews(currentProject?.id);
|
||||||
|
|
||||||
// History-aware view change handler — pushes nav entry on mobile.
|
// History-aware view change handler — pushes nav entry on back-navigation stack.
|
||||||
const handleTaskViewChange = useCallback((newView: TaskView) => {
|
const handleTaskViewChange = useCallback((newView: TaskView) => {
|
||||||
if (newView === "missions") {
|
if (newView === "missions") {
|
||||||
setMissionResumeSessionId(undefined);
|
setMissionResumeSessionId(undefined);
|
||||||
@@ -254,7 +254,6 @@ function AppInner() {
|
|||||||
}
|
}
|
||||||
const previousView = taskView;
|
const previousView = taskView;
|
||||||
handleChangeTaskView(newView);
|
handleChangeTaskView(newView);
|
||||||
// pushNav reads enabledRef internally; isMobile not needed in deps.
|
|
||||||
if (previousView !== newView) {
|
if (previousView !== newView) {
|
||||||
pushNav({ type: "view", revert: () => handleChangeTaskView(previousView) });
|
pushNav({ type: "view", revert: () => handleChangeTaskView(previousView) });
|
||||||
}
|
}
|
||||||
@@ -620,7 +619,7 @@ function AppInner() {
|
|||||||
}, [nodesEnabled]);
|
}, [nodesEnabled]);
|
||||||
|
|
||||||
// History-aware nodes toggle — pushes nav entry only when opening
|
// History-aware nodes toggle — pushes nav entry only when opening
|
||||||
const handleOpenNodesWithHistory = useCallback(() => {
|
const handleOpenNodesWithNav = useCallback(() => {
|
||||||
if (!nodesEnabled) return;
|
if (!nodesEnabled) return;
|
||||||
if (!nodesOpen) {
|
if (!nodesOpen) {
|
||||||
setNodesOpen(true);
|
setNodesOpen(true);
|
||||||
@@ -630,50 +629,48 @@ function AppInner() {
|
|||||||
}
|
}
|
||||||
}, [nodesEnabled, nodesOpen, pushNav]);
|
}, [nodesEnabled, nodesOpen, pushNav]);
|
||||||
|
|
||||||
// History-aware modal open handlers — push nav entries on mobile only.
|
// History-aware modal open handlers — push nav entries for back-navigation.
|
||||||
// Desktop (isMobile=false): pushNav/replaceCurrent are no-ops.
|
const openDetailTask = useCallback((task: Task | TaskDetail, tab?: Parameters<typeof modalManager.openDetailTask>[1], opts?: { origin?: DetailTaskOrigin }) => {
|
||||||
const openDetailTaskWithHistory = useCallback((task: Task | TaskDetail, tab?: Parameters<typeof modalManager.openDetailTask>[1], opts?: { origin?: DetailTaskOrigin }) => {
|
|
||||||
modalManager.openDetailTask(task, tab, opts);
|
modalManager.openDetailTask(task, tab, opts);
|
||||||
pushNav({ type: "modal", close: modalManager.closeDetailTask });
|
pushNav({ type: "modal", close: modalManager.closeDetailTask });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openSettingsWithHistory = useCallback((section?: Parameters<typeof modalManager.openSettings>[0]) => {
|
const openSettingsWithNav = useCallback((section?: Parameters<typeof modalManager.openSettings>[0]) => {
|
||||||
modalManager.openSettings(section);
|
modalManager.openSettings(section);
|
||||||
pushNav({ type: "modal", close: handleSettingsClose });
|
pushNav({ type: "modal", close: handleSettingsClose });
|
||||||
}, [modalManager, pushNav, handleSettingsClose]);
|
}, [modalManager, pushNav, handleSettingsClose]);
|
||||||
|
|
||||||
const openNewTaskWithHistory = useCallback(() => {
|
const openNewTaskWithNav = useCallback(() => {
|
||||||
modalManager.openNewTask();
|
modalManager.openNewTask();
|
||||||
pushNav({ type: "modal", close: modalManager.closeNewTask });
|
pushNav({ type: "modal", close: modalManager.closeNewTask });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openPlanningWithHistory = useCallback(() => {
|
const openPlanningWithNav = useCallback(() => {
|
||||||
modalManager.openPlanning();
|
modalManager.openPlanning();
|
||||||
pushNav({ type: "modal", close: modalManager.closePlanning });
|
pushNav({ type: "modal", close: modalManager.closePlanning });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openPlanningWithInitialPlanWithHistory = useCallback((initialPlan: string) => {
|
const openPlanningWithInitialPlanWithNav = useCallback((initialPlan: string) => {
|
||||||
modalManager.openPlanningWithInitialPlan(initialPlan);
|
modalManager.openPlanningWithInitialPlan(initialPlan);
|
||||||
pushNav({ type: "modal", close: modalManager.closePlanning });
|
pushNav({ type: "modal", close: modalManager.closePlanning });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const resumePlanningWithHistory = useCallback(() => {
|
const resumePlanningWithNav = useCallback(() => {
|
||||||
modalManager.resumePlanning();
|
modalManager.resumePlanning();
|
||||||
pushNav({ type: "modal", close: modalManager.closePlanning });
|
pushNav({ type: "modal", close: modalManager.closePlanning });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openSubtaskBreakdownWithHistory = useCallback((description: string) => {
|
const openSubtaskBreakdownWithNav = useCallback((description: string) => {
|
||||||
modalManager.openSubtaskBreakdown(description);
|
modalManager.openSubtaskBreakdown(description);
|
||||||
pushNav({ type: "modal", close: modalManager.closeSubtask });
|
pushNav({ type: "modal", close: modalManager.closeSubtask });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openGitHubImportWithHistory = useCallback(() => {
|
const openGitHubImportWithNav = useCallback(() => {
|
||||||
modalManager.openGitHubImport();
|
modalManager.openGitHubImport();
|
||||||
pushNav({ type: "modal", close: modalManager.closeGitHubImport });
|
pushNav({ type: "modal", close: modalManager.closeGitHubImport });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const toggleTerminalWithHistory = useCallback(() => {
|
const toggleTerminalWithNav = useCallback(() => {
|
||||||
// Only push if terminal is currently closed (opening)
|
|
||||||
if (!modalManager.terminalOpen) {
|
if (!modalManager.terminalOpen) {
|
||||||
modalManager.toggleTerminal();
|
modalManager.toggleTerminal();
|
||||||
pushNav({ type: "modal", close: modalManager.closeTerminal });
|
pushNav({ type: "modal", close: modalManager.closeTerminal });
|
||||||
@@ -682,59 +679,59 @@ function AppInner() {
|
|||||||
}
|
}
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openFilesWithHistory = useCallback(() => {
|
const openFilesWithNav = useCallback(() => {
|
||||||
modalManager.openFiles();
|
modalManager.openFiles();
|
||||||
pushNav({ type: "modal", close: modalManager.closeFiles });
|
pushNav({ type: "modal", close: modalManager.closeFiles });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openTodosWithHistory = useCallback(() => {
|
const openTodosWithNav = useCallback(() => {
|
||||||
modalManager.openTodos();
|
modalManager.openTodos();
|
||||||
pushNav({ type: "modal", close: modalManager.closeTodos });
|
pushNav({ type: "modal", close: modalManager.closeTodos });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openActivityLogWithHistory = useCallback(() => {
|
const openActivityLogWithNav = useCallback(() => {
|
||||||
modalManager.openActivityLog();
|
modalManager.openActivityLog();
|
||||||
pushNav({ type: "modal", close: modalManager.closeActivityLog });
|
pushNav({ type: "modal", close: modalManager.closeActivityLog });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openGitManagerWithHistory = useCallback(() => {
|
const openGitManagerWithNav = useCallback(() => {
|
||||||
modalManager.openGitManager();
|
modalManager.openGitManager();
|
||||||
pushNav({ type: "modal", close: modalManager.closeGitManager });
|
pushNav({ type: "modal", close: modalManager.closeGitManager });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openSystemStatsWithHistory = useCallback(() => {
|
const openSystemStatsWithNav = useCallback(() => {
|
||||||
modalManager.openSystemStats();
|
modalManager.openSystemStats();
|
||||||
pushNav({ type: "modal", close: modalManager.closeSystemStats });
|
pushNav({ type: "modal", close: modalManager.closeSystemStats });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openSchedulesWithHistory = useCallback(() => {
|
const openSchedulesWithNav = useCallback(() => {
|
||||||
modalManager.openSchedules();
|
modalManager.openSchedules();
|
||||||
pushNav({ type: "modal", close: modalManager.closeSchedules });
|
pushNav({ type: "modal", close: modalManager.closeSchedules });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openScriptsWithHistory = useCallback(() => {
|
const openScriptsWithNav = useCallback(() => {
|
||||||
modalManager.openScripts();
|
modalManager.openScripts();
|
||||||
pushNav({ type: "modal", close: modalManager.closeScripts });
|
pushNav({ type: "modal", close: modalManager.closeScripts });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openWorkflowStepsWithHistory = useCallback(() => {
|
const openWorkflowStepsWithNav = useCallback(() => {
|
||||||
modalManager.openWorkflowSteps();
|
modalManager.openWorkflowSteps();
|
||||||
pushNav({ type: "modal", close: modalManager.closeWorkflowSteps });
|
pushNav({ type: "modal", close: modalManager.closeWorkflowSteps });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
const openUsageWithHistory = useCallback((anchorRect?: DOMRect | null) => {
|
const openUsageWithNav = useCallback((anchorRect?: DOMRect | null) => {
|
||||||
modalManager.openUsage(anchorRect);
|
modalManager.openUsage(anchorRect);
|
||||||
pushNav({ type: "modal", close: modalManager.closeUsage });
|
pushNav({ type: "modal", close: modalManager.closeUsage });
|
||||||
}, [modalManager, pushNav]);
|
}, [modalManager, pushNav]);
|
||||||
|
|
||||||
// Modal-to-modal transition: scripts -> terminal uses replaceCurrent
|
// Modal-to-modal transition: scripts -> terminal uses replaceCurrent
|
||||||
const runScriptWithHistory = useCallback(async (name: string, command: string) => {
|
const runScriptWithNav = useCallback(async (name: string, command: string) => {
|
||||||
await modalManager.runScript(name, command);
|
await modalManager.runScript(name, command);
|
||||||
replaceCurrent({ type: "modal", close: modalManager.closeTerminal });
|
replaceCurrent({ type: "modal", close: modalManager.closeTerminal });
|
||||||
}, [modalManager, replaceCurrent]);
|
}, [modalManager, replaceCurrent]);
|
||||||
|
|
||||||
// Modal-to-modal transition: settings -> onboarding uses replaceCurrent
|
// Modal-to-modal transition: settings -> onboarding uses replaceCurrent
|
||||||
const reopenOnboardingWithHistory = useCallback(() => {
|
const reopenOnboardingWithNav = useCallback(() => {
|
||||||
modalManager.closeSettings();
|
modalManager.closeSettings();
|
||||||
modalManager.openModelOnboarding();
|
modalManager.openModelOnboarding();
|
||||||
replaceCurrent({ type: "modal", close: modalManager.closeModelOnboarding });
|
replaceCurrent({ type: "modal", close: modalManager.closeModelOnboarding });
|
||||||
@@ -878,14 +875,12 @@ function AppInner() {
|
|||||||
projectId: currentProject?.id,
|
projectId: currentProject?.id,
|
||||||
tasks: isRemote && remoteData.tasks.length > 0 ? remoteData.tasks : tasks,
|
tasks: isRemote && remoteData.tasks.length > 0 ? remoteData.tasks : tasks,
|
||||||
workflowSteps,
|
workflowSteps,
|
||||||
openTaskDetail: isMobile
|
openTaskDetail: (task: Task | TaskDetail, initialTab?: DetailTaskTab) => openDetailTask(task, initialTab),
|
||||||
? (task: Task | TaskDetail, initialTab?: DetailTaskTab) => openDetailTaskWithHistory(task, initialTab)
|
|
||||||
: (task: Task | TaskDetail, initialTab?: DetailTaskTab) => modalManager.openDetailTask(task, initialTab),
|
|
||||||
renderTaskCard: (task: Task | TaskDetail) => (
|
renderTaskCard: (task: Task | TaskDetail) => (
|
||||||
<TaskCard
|
<TaskCard
|
||||||
task={task}
|
task={task}
|
||||||
projectId={currentProject?.id}
|
projectId={currentProject?.id}
|
||||||
onOpenDetail={isMobile ? (value: Task | TaskDetail) => openDetailTaskWithHistory(value) : (value: Task | TaskDetail) => modalManager.openDetailTask(value)}
|
onOpenDetail={(value: Task | TaskDetail) => openDetailTask(value)}
|
||||||
addToast={addToast}
|
addToast={addToast}
|
||||||
workflowStepNameLookup={workflowStepNameLookup}
|
workflowStepNameLookup={workflowStepNameLookup}
|
||||||
/>
|
/>
|
||||||
@@ -964,7 +959,7 @@ function AppInner() {
|
|||||||
projectId={currentProject?.id}
|
projectId={currentProject?.id}
|
||||||
onSelectTask={(taskId) => {
|
onSelectTask={(taskId) => {
|
||||||
const task = tasks.find((t) => t.id === taskId);
|
const task = tasks.find((t) => t.id === taskId);
|
||||||
if (task) (isMobile ? openDetailTaskWithHistory : modalManager.openDetailTask)(task as TaskDetail);
|
if (task) openDetailTask(task as TaskDetail);
|
||||||
}}
|
}}
|
||||||
availableTasks={tasks.map((t) => ({ id: t.id, title: t.title }))}
|
availableTasks={tasks.map((t) => ({ id: t.id, title: t.title }))}
|
||||||
resumeSessionId={missionResumeSessionId}
|
resumeSessionId={missionResumeSessionId}
|
||||||
@@ -998,7 +993,7 @@ function AppInner() {
|
|||||||
<DocumentsView
|
<DocumentsView
|
||||||
projectId={currentProject?.id}
|
projectId={currentProject?.id}
|
||||||
addToast={addToast}
|
addToast={addToast}
|
||||||
onOpenDetail={isMobile ? openDetailTaskWithHistory : modalManager.openDetailTask}
|
onOpenDetail={openDetailTask}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</PageErrorBoundary>
|
</PageErrorBoundary>
|
||||||
@@ -1076,12 +1071,12 @@ function AppInner() {
|
|||||||
maxConcurrent={maxConcurrent}
|
maxConcurrent={maxConcurrent}
|
||||||
onMoveTask={moveTask}
|
onMoveTask={moveTask}
|
||||||
onPauseTask={pauseTask}
|
onPauseTask={pauseTask}
|
||||||
onOpenDetail={isMobile ? openDetailTaskWithHistory : modalManager.openDetailTask}
|
onOpenDetail={openDetailTask}
|
||||||
addToast={addToast}
|
addToast={addToast}
|
||||||
onQuickCreate={handleBoardQuickCreate}
|
onQuickCreate={handleBoardQuickCreate}
|
||||||
onNewTask={isMobile ? openNewTaskWithHistory : modalManager.openNewTask}
|
onNewTask={openNewTaskWithNav}
|
||||||
onPlanningMode={isMobile ? openPlanningWithInitialPlanWithHistory : modalManager.openPlanningWithInitialPlan}
|
onPlanningMode={openPlanningWithInitialPlanWithNav}
|
||||||
onSubtaskBreakdown={isMobile ? openSubtaskBreakdownWithHistory : modalManager.openSubtaskBreakdown}
|
onSubtaskBreakdown={openSubtaskBreakdownWithNav}
|
||||||
autoMerge={autoMerge}
|
autoMerge={autoMerge}
|
||||||
onToggleAutoMerge={toggleAutoMerge}
|
onToggleAutoMerge={toggleAutoMerge}
|
||||||
globalPaused={globalPaused}
|
globalPaused={globalPaused}
|
||||||
@@ -1119,13 +1114,13 @@ function AppInner() {
|
|||||||
onMergeTask={mergeTask}
|
onMergeTask={mergeTask}
|
||||||
onResetTask={resetTask}
|
onResetTask={resetTask}
|
||||||
onDuplicateTask={duplicateTask}
|
onDuplicateTask={duplicateTask}
|
||||||
onOpenDetail={isMobile ? (task, options) => openDetailTaskWithHistory(task, undefined, options) : (task, options) => modalManager.openDetailTask(task, undefined, options)}
|
onOpenDetail={(task, options) => openDetailTask(task, undefined, options)}
|
||||||
addToast={addToast}
|
addToast={addToast}
|
||||||
globalPaused={globalPaused}
|
globalPaused={globalPaused}
|
||||||
onNewTask={isMobile ? openNewTaskWithHistory : modalManager.openNewTask}
|
onNewTask={openNewTaskWithNav}
|
||||||
onQuickCreate={handleBoardQuickCreate}
|
onQuickCreate={handleBoardQuickCreate}
|
||||||
onPlanningMode={isMobile ? openPlanningWithInitialPlanWithHistory : modalManager.openPlanningWithInitialPlan}
|
onPlanningMode={openPlanningWithInitialPlanWithNav}
|
||||||
onSubtaskBreakdown={isMobile ? openSubtaskBreakdownWithHistory : modalManager.openSubtaskBreakdown}
|
onSubtaskBreakdown={openSubtaskBreakdownWithNav}
|
||||||
availableModels={availableModels}
|
availableModels={availableModels}
|
||||||
favoriteProviders={favoriteProviders}
|
favoriteProviders={favoriteProviders}
|
||||||
favoriteModels={favoriteModels}
|
favoriteModels={favoriteModels}
|
||||||
@@ -1160,27 +1155,27 @@ function AppInner() {
|
|||||||
<>
|
<>
|
||||||
<Header
|
<Header
|
||||||
isElectron={isElectron}
|
isElectron={isElectron}
|
||||||
onOpenSettings={isMobile ? openSettingsWithHistory : handleOpenSettings}
|
onOpenSettings={openSettingsWithNav}
|
||||||
onOpenGitHubImport={isMobile ? openGitHubImportWithHistory : modalManager.openGitHubImport}
|
onOpenGitHubImport={openGitHubImportWithNav}
|
||||||
onOpenPlanning={isMobile ? openPlanningWithHistory : modalManager.openPlanning}
|
onOpenPlanning={openPlanningWithNav}
|
||||||
onResumePlanning={isMobile ? resumePlanningWithHistory : modalManager.resumePlanning}
|
onResumePlanning={resumePlanningWithNav}
|
||||||
activePlanningSessionCount={bgPlanningSessions.length}
|
activePlanningSessionCount={bgPlanningSessions.length}
|
||||||
onOpenUsage={isMobile ? openUsageWithHistory : modalManager.openUsage}
|
onOpenUsage={openUsageWithNav}
|
||||||
onOpenActivityLog={isMobile ? openActivityLogWithHistory : modalManager.openActivityLog}
|
onOpenActivityLog={openActivityLogWithNav}
|
||||||
onOpenSystemStats={isMobile ? openSystemStatsWithHistory : modalManager.openSystemStats}
|
onOpenSystemStats={openSystemStatsWithNav}
|
||||||
onOpenMailbox={() => handleTaskViewChange("mailbox")}
|
onOpenMailbox={() => handleTaskViewChange("mailbox")}
|
||||||
mailboxUnreadCount={mailboxUnreadCount}
|
mailboxUnreadCount={mailboxUnreadCount}
|
||||||
onOpenSchedules={isMobile ? openSchedulesWithHistory : modalManager.openSchedules}
|
onOpenSchedules={openSchedulesWithNav}
|
||||||
onOpenGitManager={isMobile ? openGitManagerWithHistory : modalManager.openGitManager}
|
onOpenGitManager={openGitManagerWithNav}
|
||||||
onOpenNodes={isMobile ? handleOpenNodesWithHistory : handleOpenNodes}
|
onOpenNodes={handleOpenNodesWithNav}
|
||||||
showNodesButton={nodesEnabled}
|
showNodesButton={nodesEnabled}
|
||||||
onOpenWorkflowSteps={isMobile ? openWorkflowStepsWithHistory : modalManager.openWorkflowSteps}
|
onOpenWorkflowSteps={openWorkflowStepsWithNav}
|
||||||
onOpenScripts={isMobile ? openScriptsWithHistory : modalManager.openScripts}
|
onOpenScripts={openScriptsWithNav}
|
||||||
onRunScript={isMobile ? runScriptWithHistory : modalManager.runScript}
|
onRunScript={runScriptWithNav}
|
||||||
onToggleTerminal={isMobile ? toggleTerminalWithHistory : modalManager.toggleTerminal}
|
onToggleTerminal={toggleTerminalWithNav}
|
||||||
onOpenFiles={isMobile ? openFilesWithHistory : modalManager.openFiles}
|
onOpenFiles={openFilesWithNav}
|
||||||
filesOpen={modalManager.filesOpen}
|
filesOpen={modalManager.filesOpen}
|
||||||
onOpenTodos={isMobile ? openTodosWithHistory : modalManager.openTodos}
|
onOpenTodos={openTodosWithNav}
|
||||||
todosOpen={modalManager.todosOpen}
|
todosOpen={modalManager.todosOpen}
|
||||||
todosEnabled={todosEnabled}
|
todosEnabled={todosEnabled}
|
||||||
globalPaused={globalPaused}
|
globalPaused={globalPaused}
|
||||||
@@ -1285,27 +1280,27 @@ function AppInner() {
|
|||||||
footerVisible={viewMode === "project" && !!currentProject}
|
footerVisible={viewMode === "project" && !!currentProject}
|
||||||
modalOpen={modalManager.anyModalOpen}
|
modalOpen={modalManager.anyModalOpen}
|
||||||
keyboardOpen={mobileKeyboardOpen}
|
keyboardOpen={mobileKeyboardOpen}
|
||||||
onOpenSettings={isMobile ? openSettingsWithHistory : handleOpenSettings}
|
onOpenSettings={openSettingsWithNav}
|
||||||
onOpenActivityLog={isMobile ? openActivityLogWithHistory : modalManager.openActivityLog}
|
onOpenActivityLog={openActivityLogWithNav}
|
||||||
onOpenSystemStats={isMobile ? openSystemStatsWithHistory : modalManager.openSystemStats}
|
onOpenSystemStats={openSystemStatsWithNav}
|
||||||
onOpenMailbox={() => handleTaskViewChange("mailbox")}
|
onOpenMailbox={() => handleTaskViewChange("mailbox")}
|
||||||
onOpenNodes={isMobile ? handleOpenNodesWithHistory : handleOpenNodes}
|
onOpenNodes={handleOpenNodesWithNav}
|
||||||
mailboxUnreadCount={mailboxUnreadCount}
|
mailboxUnreadCount={mailboxUnreadCount}
|
||||||
onOpenGitManager={isMobile ? openGitManagerWithHistory : modalManager.openGitManager}
|
onOpenGitManager={openGitManagerWithNav}
|
||||||
onOpenWorkflowSteps={isMobile ? openWorkflowStepsWithHistory : modalManager.openWorkflowSteps}
|
onOpenWorkflowSteps={openWorkflowStepsWithNav}
|
||||||
onOpenSchedules={isMobile ? openSchedulesWithHistory : modalManager.openSchedules}
|
onOpenSchedules={openSchedulesWithNav}
|
||||||
onOpenScripts={isMobile ? openScriptsWithHistory : modalManager.openScripts}
|
onOpenScripts={openScriptsWithNav}
|
||||||
onToggleTerminal={isMobile ? toggleTerminalWithHistory : modalManager.toggleTerminal}
|
onToggleTerminal={toggleTerminalWithNav}
|
||||||
onOpenFiles={isMobile ? openFilesWithHistory : modalManager.openFiles}
|
onOpenFiles={openFilesWithNav}
|
||||||
onOpenTodos={isMobile ? openTodosWithHistory : modalManager.openTodos}
|
onOpenTodos={openTodosWithNav}
|
||||||
todosOpen={modalManager.todosOpen}
|
todosOpen={modalManager.todosOpen}
|
||||||
onOpenGitHubImport={isMobile ? openGitHubImportWithHistory : modalManager.openGitHubImport}
|
onOpenGitHubImport={openGitHubImportWithNav}
|
||||||
onOpenPlanning={isMobile ? openPlanningWithHistory : modalManager.openPlanning}
|
onOpenPlanning={openPlanningWithNav}
|
||||||
onResumePlanning={isMobile ? resumePlanningWithHistory : modalManager.resumePlanning}
|
onResumePlanning={resumePlanningWithNav}
|
||||||
activePlanningSessionCount={bgPlanningSessions.length}
|
activePlanningSessionCount={bgPlanningSessions.length}
|
||||||
onOpenUsage={isMobile ? () => openUsageWithHistory(null) : () => modalManager.openUsage(null)}
|
onOpenUsage={() => openUsageWithNav(null)}
|
||||||
onViewAllProjects={handleViewAllProjects}
|
onViewAllProjects={handleViewAllProjects}
|
||||||
onRunScript={isMobile ? runScriptWithHistory : modalManager.runScript}
|
onRunScript={runScriptWithNav}
|
||||||
projectId={currentProject?.id}
|
projectId={currentProject?.id}
|
||||||
showSkillsTab={skillsEnabled}
|
showSkillsTab={skillsEnabled}
|
||||||
experimentalFeatures={{
|
experimentalFeatures={{
|
||||||
@@ -1357,10 +1352,7 @@ function AppInner() {
|
|||||||
deepLink={{ handleDetailClose }}
|
deepLink={{ handleDetailClose }}
|
||||||
settings={{ prAuthAvailable, themeMode, colorTheme, dashboardFontScalePct, setThemeMode, setColorTheme, setDashboardFontScalePct }}
|
settings={{ prAuthAvailable, themeMode, colorTheme, dashboardFontScalePct, setThemeMode, setColorTheme, setDashboardFontScalePct }}
|
||||||
onSettingsClose={handleSettingsClose}
|
onSettingsClose={handleSettingsClose}
|
||||||
onReopenOnboarding={isMobile ? reopenOnboardingWithHistory : () => {
|
onReopenOnboarding={reopenOnboardingWithNav}
|
||||||
modalManager.closeSettings();
|
|
||||||
modalManager.openModelOnboarding();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<AuthTokenRecoveryDialog open={authTokenRecoveryOpen} />
|
<AuthTokenRecoveryDialog open={authTokenRecoveryOpen} />
|
||||||
{shellApi && (
|
{shellApi && (
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ export interface UseNavigationHistoryResult {
|
|||||||
* Centralized back-navigation hook that integrates the browser History API
|
* Centralized back-navigation hook that integrates the browser History API
|
||||||
* (`pushState`/`popstate`) with modal and view state machines.
|
* (`pushState`/`popstate`) with modal and view state machines.
|
||||||
*
|
*
|
||||||
* On mobile, every modal open and view change pushes a history entry so that
|
* Every modal open and view change pushes a history entry so that the
|
||||||
* the Android hardware back button and iOS swipe-back gesture dismiss the
|
* browser back button dismisses the top modal or reverts to the previous
|
||||||
* top modal or revert to the previous view.
|
* view. Works on both desktop and mobile.
|
||||||
*
|
*
|
||||||
* On desktop (`enabled: false`), all operations are no-ops and no `popstate`
|
* When `enabled` is false, all operations are no-ops and no `popstate`
|
||||||
* listener is registered, leaving desktop behavior completely unchanged.
|
* listener is registered.
|
||||||
*/
|
*/
|
||||||
export function useNavigationHistory(
|
export function useNavigationHistory(
|
||||||
options: UseNavigationHistoryOptions,
|
options: UseNavigationHistoryOptions,
|
||||||
|
|||||||
Reference in New Issue
Block a user