mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 14:19:24 +00:00
Circular dependencies with navigation fixes, types broken out
This commit is contained in:
parent
263efa273c
commit
ea7c8ee1c7
@ -1,5 +1,6 @@
|
|||||||
import React, { createContext, useContext, useReducer, useCallback } from 'react';
|
import React, { createContext, useContext, useReducer, useCallback } from 'react';
|
||||||
import { useNavigationUrlSync } from '../hooks/useUrlSync';
|
import { useNavigationUrlSync } from '../hooks/useUrlSync';
|
||||||
|
import { ModeType, isValidMode, getDefaultMode } from '../types/navigation';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NavigationContext - Complete navigation management system
|
* NavigationContext - Complete navigation management system
|
||||||
@ -9,32 +10,13 @@ import { useNavigationUrlSync } from '../hooks/useUrlSync';
|
|||||||
* maintain clear separation of concerns.
|
* maintain clear separation of concerns.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Navigation mode types - complete list to match fileContext.ts
|
|
||||||
export type ModeType =
|
|
||||||
| 'viewer'
|
|
||||||
| 'pageEditor'
|
|
||||||
| 'fileEditor'
|
|
||||||
| 'merge'
|
|
||||||
| 'split'
|
|
||||||
| 'compress'
|
|
||||||
| 'ocr'
|
|
||||||
| 'convert'
|
|
||||||
| 'sanitize'
|
|
||||||
| 'addPassword'
|
|
||||||
| 'changePermissions'
|
|
||||||
| 'addWatermark'
|
|
||||||
| 'removePassword'
|
|
||||||
| 'single-large-page'
|
|
||||||
| 'repair'
|
|
||||||
| 'unlockPdfForms'
|
|
||||||
| 'removeCertificateSign';
|
|
||||||
|
|
||||||
// Navigation state
|
// Navigation state
|
||||||
interface NavigationState {
|
interface NavigationState {
|
||||||
currentMode: ModeType;
|
currentMode: ModeType;
|
||||||
hasUnsavedChanges: boolean;
|
hasUnsavedChanges: boolean;
|
||||||
pendingNavigation: (() => void) | null;
|
pendingNavigation: (() => void) | null;
|
||||||
showNavigationWarning: boolean;
|
showNavigationWarning: boolean;
|
||||||
|
selectedToolKey: string | null; // Add tool selection to navigation state
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation actions
|
// Navigation actions
|
||||||
@ -42,7 +24,8 @@ type NavigationAction =
|
|||||||
| { type: 'SET_MODE'; payload: { mode: ModeType } }
|
| { type: 'SET_MODE'; payload: { mode: ModeType } }
|
||||||
| { type: 'SET_UNSAVED_CHANGES'; payload: { hasChanges: boolean } }
|
| { type: 'SET_UNSAVED_CHANGES'; payload: { hasChanges: boolean } }
|
||||||
| { type: 'SET_PENDING_NAVIGATION'; payload: { navigationFn: (() => void) | null } }
|
| { type: 'SET_PENDING_NAVIGATION'; payload: { navigationFn: (() => void) | null } }
|
||||||
| { type: 'SHOW_NAVIGATION_WARNING'; payload: { show: boolean } };
|
| { type: 'SHOW_NAVIGATION_WARNING'; payload: { show: boolean } }
|
||||||
|
| { type: 'SET_SELECTED_TOOL'; payload: { toolKey: string | null } };
|
||||||
|
|
||||||
// Navigation reducer
|
// Navigation reducer
|
||||||
const navigationReducer = (state: NavigationState, action: NavigationAction): NavigationState => {
|
const navigationReducer = (state: NavigationState, action: NavigationAction): NavigationState => {
|
||||||
@ -59,6 +42,9 @@ const navigationReducer = (state: NavigationState, action: NavigationAction): Na
|
|||||||
case 'SHOW_NAVIGATION_WARNING':
|
case 'SHOW_NAVIGATION_WARNING':
|
||||||
return { ...state, showNavigationWarning: action.payload.show };
|
return { ...state, showNavigationWarning: action.payload.show };
|
||||||
|
|
||||||
|
case 'SET_SELECTED_TOOL':
|
||||||
|
return { ...state, selectedToolKey: action.payload.toolKey };
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@ -66,10 +52,11 @@ const navigationReducer = (state: NavigationState, action: NavigationAction): Na
|
|||||||
|
|
||||||
// Initial state
|
// Initial state
|
||||||
const initialState: NavigationState = {
|
const initialState: NavigationState = {
|
||||||
currentMode: 'pageEditor',
|
currentMode: getDefaultMode(),
|
||||||
hasUnsavedChanges: false,
|
hasUnsavedChanges: false,
|
||||||
pendingNavigation: null,
|
pendingNavigation: null,
|
||||||
showNavigationWarning: false
|
showNavigationWarning: false,
|
||||||
|
selectedToolKey: null
|
||||||
};
|
};
|
||||||
|
|
||||||
// Navigation context actions interface
|
// Navigation context actions interface
|
||||||
@ -80,6 +67,9 @@ export interface NavigationContextActions {
|
|||||||
requestNavigation: (navigationFn: () => void) => void;
|
requestNavigation: (navigationFn: () => void) => void;
|
||||||
confirmNavigation: () => void;
|
confirmNavigation: () => void;
|
||||||
cancelNavigation: () => void;
|
cancelNavigation: () => void;
|
||||||
|
selectTool: (toolKey: string) => void;
|
||||||
|
clearToolSelection: () => void;
|
||||||
|
handleToolSelect: (toolId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split context values
|
// Split context values
|
||||||
@ -88,6 +78,7 @@ export interface NavigationContextStateValue {
|
|||||||
hasUnsavedChanges: boolean;
|
hasUnsavedChanges: boolean;
|
||||||
pendingNavigation: (() => void) | null;
|
pendingNavigation: (() => void) | null;
|
||||||
showNavigationWarning: boolean;
|
showNavigationWarning: boolean;
|
||||||
|
selectedToolKey: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NavigationContextActionsValue {
|
export interface NavigationContextActionsValue {
|
||||||
@ -145,6 +136,31 @@ export const NavigationProvider: React.FC<{
|
|||||||
// Clear navigation without executing
|
// Clear navigation without executing
|
||||||
dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } });
|
dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } });
|
||||||
dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: false } });
|
dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: false } });
|
||||||
|
}, []),
|
||||||
|
|
||||||
|
selectTool: useCallback((toolKey: string) => {
|
||||||
|
dispatch({ type: 'SET_SELECTED_TOOL', payload: { toolKey } });
|
||||||
|
}, []),
|
||||||
|
|
||||||
|
clearToolSelection: useCallback(() => {
|
||||||
|
dispatch({ type: 'SET_SELECTED_TOOL', payload: { toolKey: null } });
|
||||||
|
}, []),
|
||||||
|
|
||||||
|
handleToolSelect: useCallback((toolId: string) => {
|
||||||
|
// Handle special cases
|
||||||
|
if (toolId === 'allTools') {
|
||||||
|
dispatch({ type: 'SET_SELECTED_TOOL', payload: { toolKey: null } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special-case: if tool is a dedicated reader tool, enter reader mode
|
||||||
|
if (toolId === 'read' || toolId === 'view-pdf') {
|
||||||
|
dispatch({ type: 'SET_SELECTED_TOOL', payload: { toolKey: null } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ type: 'SET_SELECTED_TOOL', payload: { toolKey: toolId } });
|
||||||
|
dispatch({ type: 'SET_MODE', payload: { mode: 'fileEditor' as ModeType } });
|
||||||
}, [])
|
}, [])
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -152,7 +168,8 @@ export const NavigationProvider: React.FC<{
|
|||||||
currentMode: state.currentMode,
|
currentMode: state.currentMode,
|
||||||
hasUnsavedChanges: state.hasUnsavedChanges,
|
hasUnsavedChanges: state.hasUnsavedChanges,
|
||||||
pendingNavigation: state.pendingNavigation,
|
pendingNavigation: state.pendingNavigation,
|
||||||
showNavigationWarning: state.showNavigationWarning
|
showNavigationWarning: state.showNavigationWarning,
|
||||||
|
selectedToolKey: state.selectedToolKey
|
||||||
};
|
};
|
||||||
|
|
||||||
const actionsValue: NavigationContextActionsValue = {
|
const actionsValue: NavigationContextActionsValue = {
|
||||||
@ -212,16 +229,8 @@ export const useNavigationGuard = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Utility functions for mode handling
|
// Re-export utility functions from types for backward compatibility
|
||||||
export const isValidMode = (mode: string): mode is ModeType => {
|
export { isValidMode, getDefaultMode, type ModeType } from '../types/navigation';
|
||||||
const validModes: ModeType[] = [
|
|
||||||
'viewer', 'pageEditor', 'fileEditor', 'merge', 'split',
|
|
||||||
'compress', 'ocr', 'convert', 'addPassword', 'changePermissions', 'sanitize'
|
|
||||||
];
|
|
||||||
return validModes.includes(mode as ModeType);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDefaultMode = (): ModeType => 'pageEditor';
|
|
||||||
|
|
||||||
// TODO: This will be expanded for URL-based routing system
|
// TODO: This will be expanded for URL-based routing system
|
||||||
// - URL parsing utilities
|
// - URL parsing utilities
|
||||||
|
@ -8,7 +8,7 @@ import { useToolManagement } from '../hooks/useToolManagement';
|
|||||||
import { PageEditorFunctions } from '../types/pageEditor';
|
import { PageEditorFunctions } from '../types/pageEditor';
|
||||||
import { ToolRegistryEntry } from '../data/toolsTaxonomy';
|
import { ToolRegistryEntry } from '../data/toolsTaxonomy';
|
||||||
import { useToolWorkflowUrlSync } from '../hooks/useUrlSync';
|
import { useToolWorkflowUrlSync } from '../hooks/useUrlSync';
|
||||||
import { useNavigationActions } from './NavigationContext';
|
import { useNavigationActions, useNavigationState } from './NavigationContext';
|
||||||
|
|
||||||
// State interface
|
// State interface
|
||||||
interface ToolWorkflowState {
|
interface ToolWorkflowState {
|
||||||
@ -106,22 +106,19 @@ interface ToolWorkflowProviderProps {
|
|||||||
export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
||||||
const [state, dispatch] = useReducer(toolWorkflowReducer, initialState);
|
const [state, dispatch] = useReducer(toolWorkflowReducer, initialState);
|
||||||
|
|
||||||
// File context for view changes
|
// Navigation actions and state are available since we're inside NavigationProvider
|
||||||
const { actions } = useNavigationActions();
|
const { actions } = useNavigationActions();
|
||||||
// Wrapper to convert string to ModeType
|
const navigationState = useNavigationState();
|
||||||
const handleViewChange = (view: string) => {
|
|
||||||
actions.setMode(view as any); // ToolWorkflowContext should validate this
|
|
||||||
};
|
|
||||||
|
|
||||||
// Tool management hook
|
// Tool management hook
|
||||||
const {
|
const {
|
||||||
selectedToolKey,
|
|
||||||
selectedTool,
|
|
||||||
toolRegistry,
|
toolRegistry,
|
||||||
selectTool,
|
getSelectedTool,
|
||||||
clearToolSelection,
|
|
||||||
} = useToolManagement();
|
} = useToolManagement();
|
||||||
|
|
||||||
|
// Get selected tool from navigation context
|
||||||
|
const selectedTool = getSelectedTool(navigationState.selectedToolKey);
|
||||||
|
|
||||||
// UI Action creators
|
// UI Action creators
|
||||||
const setSidebarsVisible = useCallback((visible: boolean) => {
|
const setSidebarsVisible = useCallback((visible: boolean) => {
|
||||||
dispatch({ type: 'SET_SIDEBARS_VISIBLE', payload: visible });
|
dispatch({ type: 'SET_SIDEBARS_VISIBLE', payload: visible });
|
||||||
@ -149,28 +146,27 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
|||||||
|
|
||||||
// Workflow actions (compound actions that coordinate multiple state changes)
|
// Workflow actions (compound actions that coordinate multiple state changes)
|
||||||
const handleToolSelect = useCallback((toolId: string) => {
|
const handleToolSelect = useCallback((toolId: string) => {
|
||||||
// Special-case: if tool is a dedicated reader tool, enter reader mode and do not go to toolContent
|
actions.handleToolSelect(toolId);
|
||||||
if (toolId === 'read' || toolId === 'view-pdf') {
|
|
||||||
setReaderMode(true);
|
|
||||||
setLeftPanelView('toolPicker');
|
|
||||||
clearToolSelection();
|
|
||||||
setSearchQuery('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectTool(toolId);
|
// Clear search query when selecting a tool
|
||||||
handleViewChange('fileEditor' as any); // ToolWorkflowContext should validate this
|
|
||||||
setLeftPanelView('toolContent');
|
|
||||||
setReaderMode(false);
|
|
||||||
// Clear search so the tool content becomes visible immediately
|
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
}, [selectTool, handleViewChange, setLeftPanelView, setReaderMode, setSearchQuery, clearToolSelection]);
|
|
||||||
|
// Handle view switching logic
|
||||||
|
if (toolId === 'allTools' || toolId === 'read' || toolId === 'view-pdf') {
|
||||||
|
setLeftPanelView('toolPicker');
|
||||||
|
if (toolId === 'read' || toolId === 'view-pdf') {
|
||||||
|
setReaderMode(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setLeftPanelView('toolContent');
|
||||||
|
}
|
||||||
|
}, [actions, setLeftPanelView, setReaderMode, setSearchQuery]);
|
||||||
|
|
||||||
const handleBackToTools = useCallback(() => {
|
const handleBackToTools = useCallback(() => {
|
||||||
setLeftPanelView('toolPicker');
|
setLeftPanelView('toolPicker');
|
||||||
setReaderMode(false);
|
setReaderMode(false);
|
||||||
clearToolSelection();
|
actions.clearToolSelection();
|
||||||
}, [setLeftPanelView, setReaderMode, clearToolSelection]);
|
}, [setLeftPanelView, setReaderMode, actions]);
|
||||||
|
|
||||||
const handleReaderToggle = useCallback(() => {
|
const handleReaderToggle = useCallback(() => {
|
||||||
setReaderMode(true);
|
setReaderMode(true);
|
||||||
@ -190,13 +186,13 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Enable URL synchronization for tool selection
|
// Enable URL synchronization for tool selection
|
||||||
useToolWorkflowUrlSync(selectedToolKey, selectTool, clearToolSelection, true);
|
useToolWorkflowUrlSync(navigationState.selectedToolKey, actions.selectTool, actions.clearToolSelection, true);
|
||||||
|
|
||||||
// Simple context value with basic memoization
|
// Properly memoized context value
|
||||||
const contextValue : ToolWorkflowContextValue ={
|
const contextValue = useMemo((): ToolWorkflowContextValue => ({
|
||||||
// State
|
// State
|
||||||
...state,
|
...state,
|
||||||
selectedToolKey,
|
selectedToolKey: navigationState.selectedToolKey,
|
||||||
selectedTool,
|
selectedTool,
|
||||||
toolRegistry,
|
toolRegistry,
|
||||||
|
|
||||||
@ -207,8 +203,8 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
|||||||
setPreviewFile,
|
setPreviewFile,
|
||||||
setPageEditorFunctions,
|
setPageEditorFunctions,
|
||||||
setSearchQuery,
|
setSearchQuery,
|
||||||
selectTool,
|
selectTool: actions.selectTool,
|
||||||
clearToolSelection,
|
clearToolSelection: actions.clearToolSelection,
|
||||||
|
|
||||||
// Workflow Actions
|
// Workflow Actions
|
||||||
handleToolSelect,
|
handleToolSelect,
|
||||||
@ -218,7 +214,25 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
|||||||
// Computed
|
// Computed
|
||||||
filteredTools,
|
filteredTools,
|
||||||
isPanelVisible,
|
isPanelVisible,
|
||||||
};
|
}), [
|
||||||
|
state,
|
||||||
|
navigationState.selectedToolKey,
|
||||||
|
selectedTool,
|
||||||
|
toolRegistry,
|
||||||
|
setSidebarsVisible,
|
||||||
|
setLeftPanelView,
|
||||||
|
setReaderMode,
|
||||||
|
setPreviewFile,
|
||||||
|
setPageEditorFunctions,
|
||||||
|
setSearchQuery,
|
||||||
|
actions.selectTool,
|
||||||
|
actions.clearToolSelection,
|
||||||
|
handleToolSelect,
|
||||||
|
handleBackToTools,
|
||||||
|
handleReaderToggle,
|
||||||
|
filteredTools,
|
||||||
|
isPanelVisible,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolWorkflowContext.Provider value={contextValue}>
|
<ToolWorkflowContext.Provider value={contextValue}>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useToolWorkflow } from '../contexts/ToolWorkflowContext';
|
import { useNavigationActions, useNavigationState } from '../contexts/NavigationContext';
|
||||||
|
|
||||||
// Material UI Icons
|
// Material UI Icons
|
||||||
import CompressIcon from '@mui/icons-material/Compress';
|
import CompressIcon from '@mui/icons-material/Compress';
|
||||||
@ -44,7 +44,8 @@ const ALL_SUGGESTED_TOOLS: Omit<SuggestedTool, 'navigate'>[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export function useSuggestedTools(): SuggestedTool[] {
|
export function useSuggestedTools(): SuggestedTool[] {
|
||||||
const { handleToolSelect, selectedToolKey } = useToolWorkflow();
|
const { actions } = useNavigationActions();
|
||||||
|
const { selectedToolKey } = useNavigationState();
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
// Filter out the current tool
|
// Filter out the current tool
|
||||||
@ -53,7 +54,7 @@ export function useSuggestedTools(): SuggestedTool[] {
|
|||||||
// Add navigation function to each tool
|
// Add navigation function to each tool
|
||||||
return filteredTools.map(tool => ({
|
return filteredTools.map(tool => ({
|
||||||
...tool,
|
...tool,
|
||||||
navigate: () => handleToolSelect(tool.name)
|
navigate: () => actions.handleToolSelect(tool.name)
|
||||||
}));
|
}));
|
||||||
}, [selectedToolKey, handleToolSelect]);
|
}, [selectedToolKey, actions]);
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,16 @@ import { getAllEndpoints, type ToolRegistryEntry } from "../data/toolsTaxonomy";
|
|||||||
import { useMultipleEndpointsEnabled } from "./useEndpointConfig";
|
import { useMultipleEndpointsEnabled } from "./useEndpointConfig";
|
||||||
|
|
||||||
interface ToolManagementResult {
|
interface ToolManagementResult {
|
||||||
selectedToolKey: string | null;
|
|
||||||
selectedTool: ToolRegistryEntry | null;
|
selectedTool: ToolRegistryEntry | null;
|
||||||
toolSelectedFileIds: string[];
|
toolSelectedFileIds: string[];
|
||||||
toolRegistry: Record<string, ToolRegistryEntry>;
|
toolRegistry: Record<string, ToolRegistryEntry>;
|
||||||
selectTool: (toolKey: string) => void;
|
|
||||||
clearToolSelection: () => void;
|
|
||||||
setToolSelectedFileIds: (fileIds: string[]) => void;
|
setToolSelectedFileIds: (fileIds: string[]) => void;
|
||||||
|
getSelectedTool: (toolKey: string | null) => ToolRegistryEntry | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useToolManagement = (): ToolManagementResult => {
|
export const useToolManagement = (): ToolManagementResult => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [selectedToolKey, setSelectedToolKey] = useState<string | null>(null);
|
|
||||||
const [toolSelectedFileIds, setToolSelectedFileIds] = useState<string[]>([]);
|
const [toolSelectedFileIds, setToolSelectedFileIds] = useState<string[]>([]);
|
||||||
|
|
||||||
// Build endpoints list from registry entries with fallback to legacy mapping
|
// Build endpoints list from registry entries with fallback to legacy mapping
|
||||||
@ -56,35 +53,15 @@ export const useToolManagement = (): ToolManagementResult => {
|
|||||||
return availableToolRegistry;
|
return availableToolRegistry;
|
||||||
}, [isToolAvailable, t, baseRegistry]);
|
}, [isToolAvailable, t, baseRegistry]);
|
||||||
|
|
||||||
useEffect(() => {
|
const getSelectedTool = useCallback((toolKey: string | null): ToolRegistryEntry | null => {
|
||||||
if (!endpointsLoading && selectedToolKey && !toolRegistry[selectedToolKey]) {
|
return toolKey ? toolRegistry[toolKey] || null : null;
|
||||||
const firstAvailableTool = Object.keys(toolRegistry)[0];
|
}, [toolRegistry]);
|
||||||
if (firstAvailableTool) {
|
|
||||||
setSelectedToolKey(firstAvailableTool);
|
|
||||||
} else {
|
|
||||||
setSelectedToolKey(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [endpointsLoading, selectedToolKey, toolRegistry]);
|
|
||||||
|
|
||||||
const selectTool = useCallback((toolKey: string) => {
|
|
||||||
setSelectedToolKey(toolKey);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const clearToolSelection = useCallback(() => {
|
|
||||||
setSelectedToolKey(null);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const selectedTool = selectedToolKey ? toolRegistry[selectedToolKey] : null;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedToolKey,
|
selectedTool: getSelectedTool(null), // This will be unused, kept for compatibility
|
||||||
selectedTool,
|
|
||||||
toolSelectedFileIds,
|
toolSelectedFileIds,
|
||||||
toolRegistry,
|
toolRegistry,
|
||||||
selectTool,
|
|
||||||
clearToolSelection,
|
|
||||||
setToolSelectedFileIds,
|
setToolSelectedFileIds,
|
||||||
|
getSelectedTool,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useCallback } from 'react';
|
import { useEffect, useCallback } from 'react';
|
||||||
import { ModeType } from '../contexts/NavigationContext';
|
import { ModeType } from '../types/navigation';
|
||||||
import { parseToolRoute, updateToolRoute, clearToolRoute } from '../utils/urlRouting';
|
import { parseToolRoute, updateToolRoute, clearToolRoute } from '../utils/urlRouting';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
42
frontend/src/types/navigation.ts
Normal file
42
frontend/src/types/navigation.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Shared navigation types to avoid circular dependencies
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Navigation mode types - complete list to match contexts
|
||||||
|
export type ModeType =
|
||||||
|
| 'viewer'
|
||||||
|
| 'pageEditor'
|
||||||
|
| 'fileEditor'
|
||||||
|
| 'merge'
|
||||||
|
| 'split'
|
||||||
|
| 'compress'
|
||||||
|
| 'ocr'
|
||||||
|
| 'convert'
|
||||||
|
| 'sanitize'
|
||||||
|
| 'addPassword'
|
||||||
|
| 'changePermissions'
|
||||||
|
| 'addWatermark'
|
||||||
|
| 'removePassword'
|
||||||
|
| 'single-large-page'
|
||||||
|
| 'repair'
|
||||||
|
| 'unlockPdfForms'
|
||||||
|
| 'removeCertificateSign';
|
||||||
|
|
||||||
|
// Utility functions for mode handling
|
||||||
|
export const isValidMode = (mode: string): mode is ModeType => {
|
||||||
|
const validModes: ModeType[] = [
|
||||||
|
'viewer', 'pageEditor', 'fileEditor', 'merge', 'split',
|
||||||
|
'compress', 'ocr', 'convert', 'addPassword', 'changePermissions',
|
||||||
|
'sanitize', 'addWatermark', 'removePassword', 'single-large-page',
|
||||||
|
'repair', 'unlockPdfForms', 'removeCertificateSign'
|
||||||
|
];
|
||||||
|
return validModes.includes(mode as ModeType);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDefaultMode = (): ModeType => 'pageEditor';
|
||||||
|
|
||||||
|
// Route parsing result
|
||||||
|
export interface ToolRoute {
|
||||||
|
mode: ModeType;
|
||||||
|
toolKey: string | null;
|
||||||
|
}
|
21
frontend/src/types/navigationActions.ts
Normal file
21
frontend/src/types/navigationActions.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Navigation action interfaces to break circular dependencies
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ModeType } from './navigation';
|
||||||
|
|
||||||
|
export interface NavigationActions {
|
||||||
|
setMode: (mode: ModeType) => void;
|
||||||
|
setHasUnsavedChanges: (hasChanges: boolean) => void;
|
||||||
|
showNavigationWarning: (show: boolean) => void;
|
||||||
|
requestNavigation: (navigationFn: () => void) => void;
|
||||||
|
confirmNavigation: () => void;
|
||||||
|
cancelNavigation: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NavigationState {
|
||||||
|
currentMode: ModeType;
|
||||||
|
hasUnsavedChanges: boolean;
|
||||||
|
pendingNavigation: (() => void) | null;
|
||||||
|
showNavigationWarning: boolean;
|
||||||
|
}
|
@ -3,12 +3,7 @@
|
|||||||
* Provides clean URL routing for the V2 tool system
|
* Provides clean URL routing for the V2 tool system
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ModeType } from '../contexts/NavigationContext';
|
import { ModeType, isValidMode as isValidModeType, getDefaultMode, ToolRoute } from '../types/navigation';
|
||||||
|
|
||||||
export interface ToolRoute {
|
|
||||||
mode: ModeType;
|
|
||||||
toolKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the current URL to extract tool routing information
|
* Parse the current URL to extract tool routing information
|
||||||
@ -45,7 +40,7 @@ export function parseToolRoute(): ToolRoute {
|
|||||||
|
|
||||||
// Check for query parameter fallback (e.g., ?tool=split)
|
// Check for query parameter fallback (e.g., ?tool=split)
|
||||||
const toolParam = searchParams.get('tool');
|
const toolParam = searchParams.get('tool');
|
||||||
if (toolParam && isValidMode(toolParam)) {
|
if (toolParam && isValidModeType(toolParam)) {
|
||||||
return {
|
return {
|
||||||
mode: toolParam as ModeType,
|
mode: toolParam as ModeType,
|
||||||
toolKey: toolParam
|
toolKey: toolParam
|
||||||
@ -54,7 +49,8 @@ export function parseToolRoute(): ToolRoute {
|
|||||||
|
|
||||||
// Default to page editor for home page
|
// Default to page editor for home page
|
||||||
return {
|
return {
|
||||||
mode: 'pageEditor'
|
mode: getDefaultMode(),
|
||||||
|
toolKey: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,16 +133,7 @@ export function getToolDisplayName(toolKey: string): string {
|
|||||||
return displayNames[toolKey] || toolKey;
|
return displayNames[toolKey] || toolKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Note: isValidMode is now imported from types/navigation.ts
|
||||||
* Check if a mode is valid
|
|
||||||
*/
|
|
||||||
function isValidMode(mode: string): mode is ModeType {
|
|
||||||
const validModes: ModeType[] = [
|
|
||||||
'viewer', 'pageEditor', 'fileEditor', 'merge', 'split',
|
|
||||||
'compress', 'ocr', 'convert', 'addPassword', 'changePermissions', 'sanitize'
|
|
||||||
];
|
|
||||||
return validModes.includes(mode as ModeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate shareable URL for current tool state
|
* Generate shareable URL for current tool state
|
||||||
|
Loading…
x
Reference in New Issue
Block a user