mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-03-13 02:18:16 +01:00
# Description of Changes --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
208 lines
6.5 KiB
TypeScript
208 lines
6.5 KiB
TypeScript
import React, { createContext, useContext, useCallback, useRef } from 'react';
|
|
import { useFileHandler } from '@app/hooks/useFileHandler';
|
|
import { useFilesModalContext } from '@app/contexts/FilesModalContext';
|
|
import { useNavigationActions } from '@app/contexts/NavigationContext';
|
|
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
|
|
import { useAllFiles, useFileManagement } from '@app/contexts/FileContext';
|
|
import { StirlingFile } from '@app/types/fileContext';
|
|
import { fileStorage } from '@app/services/fileStorage';
|
|
|
|
interface TourOrchestrationContextType {
|
|
// State management
|
|
saveWorkbenchState: () => void;
|
|
restoreWorkbenchState: () => Promise<void>;
|
|
|
|
// Tool deselection
|
|
backToAllTools: () => void;
|
|
|
|
// Tool selection
|
|
selectCropTool: () => void;
|
|
|
|
// File operations
|
|
loadSampleFile: () => Promise<void>;
|
|
|
|
// View switching
|
|
switchToViewer: () => void;
|
|
switchToPageEditor: () => void;
|
|
switchToActiveFiles: () => void;
|
|
|
|
// File operations
|
|
selectFirstFile: () => void;
|
|
pinFile: () => void;
|
|
|
|
// Crop settings (placeholder for now)
|
|
modifyCropSettings: () => void;
|
|
|
|
// Tool execution
|
|
executeTool: () => void;
|
|
}
|
|
|
|
const TourOrchestrationContext = createContext<TourOrchestrationContextType | undefined>(undefined);
|
|
|
|
export const TourOrchestrationProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const { addFiles } = useFileHandler();
|
|
const { closeFilesModal } = useFilesModalContext();
|
|
const { actions: navActions } = useNavigationActions();
|
|
const { handleToolSelect, handleBackToTools } = useToolWorkflow();
|
|
const { files } = useAllFiles();
|
|
const { clearAllFiles } = useFileManagement();
|
|
|
|
// Store the user's files before tour starts
|
|
const savedFilesRef = useRef<StirlingFile[]>([]);
|
|
|
|
// Keep a ref to always have the latest files
|
|
const filesRef = useRef<StirlingFile[]>(files);
|
|
React.useEffect(() => {
|
|
filesRef.current = files;
|
|
}, [files]);
|
|
|
|
const saveWorkbenchState = useCallback(() => {
|
|
// Get fresh files from ref
|
|
const currentFiles = filesRef.current;
|
|
console.log('Saving workbench state, files count:', currentFiles.length);
|
|
savedFilesRef.current = [...currentFiles];
|
|
// Clear all files for clean demo
|
|
clearAllFiles();
|
|
}, [clearAllFiles]);
|
|
|
|
const restoreWorkbenchState = useCallback(async () => {
|
|
console.log('Restoring workbench state, saved files count:', savedFilesRef.current.length);
|
|
|
|
// Go back to Tools
|
|
handleBackToTools();
|
|
|
|
// Clear all files (including tour sample)
|
|
clearAllFiles();
|
|
|
|
// Delete all active files from storage (they're just the ones from the tour)
|
|
const currentFiles = filesRef.current;
|
|
if (currentFiles.length > 0) {
|
|
try {
|
|
await Promise.all(currentFiles.map(file => fileStorage.deleteStirlingFile(file.fileId)));
|
|
console.log(`Deleted ${currentFiles.length} file(s) from storage`);
|
|
} catch (error) {
|
|
console.error('Failed to delete files from storage:', error);
|
|
}
|
|
}
|
|
|
|
// Restore saved files
|
|
if (savedFilesRef.current.length > 0) {
|
|
// Create fresh File objects from StirlingFile to avoid ID conflicts
|
|
const filesToRestore = await Promise.all(
|
|
savedFilesRef.current.map(async (sf) => {
|
|
const buffer = await sf.arrayBuffer();
|
|
return new File([buffer], sf.name, { type: sf.type, lastModified: sf.lastModified });
|
|
})
|
|
);
|
|
console.log('Restoring files:', filesToRestore.map(f => f.name));
|
|
await addFiles(filesToRestore);
|
|
savedFilesRef.current = [];
|
|
}
|
|
}, [clearAllFiles, addFiles, handleBackToTools]);
|
|
|
|
const backToAllTools = useCallback(() => {
|
|
handleBackToTools();
|
|
}, [handleBackToTools]);
|
|
|
|
const selectCropTool = useCallback(() => {
|
|
handleToolSelect('crop');
|
|
}, [handleToolSelect]);
|
|
|
|
const loadSampleFile = useCallback(async () => {
|
|
try {
|
|
const response = await fetch('/samples/Sample.pdf');
|
|
const blob = await response.blob();
|
|
const file = new File([blob], 'Sample.pdf', { type: 'application/pdf' });
|
|
|
|
await addFiles([file]);
|
|
closeFilesModal();
|
|
} catch (error) {
|
|
console.error('Failed to load sample file:', error);
|
|
}
|
|
}, [addFiles, closeFilesModal]);
|
|
|
|
const switchToViewer = useCallback(() => {
|
|
navActions.setWorkbench('viewer');
|
|
}, [navActions]);
|
|
|
|
const switchToPageEditor = useCallback(() => {
|
|
navActions.setWorkbench('pageEditor');
|
|
}, [navActions]);
|
|
|
|
const switchToActiveFiles = useCallback(() => {
|
|
navActions.setWorkbench('fileEditor');
|
|
}, [navActions]);
|
|
|
|
const selectFirstFile = useCallback(() => {
|
|
// File selection is handled by FileCard onClick
|
|
// This function could trigger a click event on the first file card
|
|
const firstFileCard = document.querySelector('[data-tour="file-card-checkbox"]') as HTMLElement;
|
|
if (firstFileCard) {
|
|
// Check if already selected (data-selected attribute)
|
|
const isSelected = firstFileCard.getAttribute('data-selected') === 'true';
|
|
// Only click if not already selected (to avoid toggling off)
|
|
if (!isSelected) {
|
|
firstFileCard.click();
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
const pinFile = useCallback(() => {
|
|
// Click the pin button directly
|
|
const pinButton = document.querySelector('[data-tour="file-card-pin"]') as HTMLElement;
|
|
if (pinButton) {
|
|
pinButton.click();
|
|
}
|
|
}, []);
|
|
|
|
const modifyCropSettings = useCallback(() => {
|
|
// Dispatch a custom event to modify crop settings
|
|
const event = new CustomEvent('tour:setCropArea', {
|
|
detail: {
|
|
x: 80,
|
|
y: 435,
|
|
width: 440,
|
|
height: 170,
|
|
}
|
|
});
|
|
window.dispatchEvent(event);
|
|
}, []);
|
|
|
|
const executeTool = useCallback(() => {
|
|
// Trigger the run button click
|
|
const runButton = document.querySelector('[data-tour="run-button"]') as HTMLElement;
|
|
if (runButton) {
|
|
runButton.click();
|
|
}
|
|
}, []);
|
|
|
|
const value: TourOrchestrationContextType = {
|
|
saveWorkbenchState,
|
|
restoreWorkbenchState,
|
|
backToAllTools,
|
|
selectCropTool,
|
|
loadSampleFile,
|
|
switchToViewer,
|
|
switchToPageEditor,
|
|
switchToActiveFiles,
|
|
selectFirstFile,
|
|
pinFile,
|
|
modifyCropSettings,
|
|
executeTool,
|
|
};
|
|
|
|
return (
|
|
<TourOrchestrationContext.Provider value={value}>
|
|
{children}
|
|
</TourOrchestrationContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useTourOrchestration = (): TourOrchestrationContextType => {
|
|
const context = useContext(TourOrchestrationContext);
|
|
if (!context) {
|
|
throw new Error('useTourOrchestration must be used within TourOrchestrationProvider');
|
|
}
|
|
return context;
|
|
};
|