mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 03:20:46 +02:00
Preserve local paths for desktop saves (#5543)
# Summary - Adds desktop file tracking: local paths are preserved and save buttons now work as expcted (doing Save/Save As as appropriate) - Adds logic to track whether files are 'dirty' (they've been modified by some tool, and not saved to disk yet). - Improves file state UX (dirty vs saved) and close warnings - Web behaviour should be unaffected by these changes ## Indicators Files now have indicators in desktop mode to tell you their state. ### File up-to-date with disk <img width="318" height="393" alt="image" src="https://github.com/user-attachments/assets/06325f9a-afd7-4c2f-8a5b-6d11e3093115" /> ### File modified by a tool but not saved to disk yet <img width="357" height="385" alt="image" src="https://github.com/user-attachments/assets/1a7716d9-c6f7-4d13-be0d-c1de6493954b" /> ### File not tracked on disk <img width="312" height="379" alt="image" src="https://github.com/user-attachments/assets/9cffe300-bd9a-4e19-97c7-9b98bebefacc" /> # Limitations - It's a bit weird that we still have files stored in indexeddb in the app, which are still loadable. We might want to change this behaviour in the future - Viewer's Save doesn't persist to disk. I've left that out here because it'd need a lot of testing to make sure the logic's right with making sure you can leave the Viewer with applying the changes to the PDF _without_ saving to disk - There's no current way to do Save As on a file that has already been persisted to disk - it's only ever Save. Similarly, there's no way to duplicate a file. --------- Co-authored-by: James Brunton <[email protected]> Co-authored-by: James Brunton <[email protected]>
This commit is contained in:
co-authored by
James Brunton
James Brunton
parent
946196de43
commit
b8ce4e47c1
@@ -14,6 +14,7 @@ import { useFileActionIcons } from '@app/hooks/useFileActionIcons';
|
||||
import { useAppConfig } from '@app/contexts/AppConfigContext';
|
||||
import { useIsMobile } from '@app/hooks/useIsMobile';
|
||||
import MobileUploadModal from '@app/components/shared/MobileUploadModal';
|
||||
import { openFilesFromDisk } from '@app/services/openFilesFromDisk';
|
||||
|
||||
const LandingPage = () => {
|
||||
const { addFiles } = useFileHandler();
|
||||
@@ -41,8 +42,14 @@ const LandingPage = () => {
|
||||
openFilesModal();
|
||||
};
|
||||
|
||||
const handleNativeUploadClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
const handleNativeUploadClick = async () => {
|
||||
const files = await openFilesFromDisk({
|
||||
multiple: true,
|
||||
onFallbackOpen: () => fileInputRef.current?.click()
|
||||
});
|
||||
if (files.length > 0) {
|
||||
await addFiles(files);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ActionIcon, Divider } from '@mantine/core';
|
||||
import '@app/components/shared/rightRail/RightRail.css';
|
||||
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
|
||||
import { useRightRail } from '@app/contexts/RightRailContext';
|
||||
import { useFileState, useFileSelection } from '@app/contexts/FileContext';
|
||||
import { useFileState, useFileSelection, useFileActions } from '@app/contexts/FileContext';
|
||||
import { useNavigationState } from '@app/contexts/NavigationContext';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
@@ -22,6 +22,7 @@ import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
import { useSidebarContext } from '@app/contexts/SidebarContext';
|
||||
import { RightRailButtonConfig, RightRailRenderContext, RightRailSection } from '@app/types/rightRail';
|
||||
import { useRightRailTooltipSide } from '@app/hooks/useRightRailTooltipSide';
|
||||
import { downloadFile } from '@app/services/downloadService';
|
||||
|
||||
const SECTION_ORDER: RightRailSection[] = ['top', 'middle', 'bottom'];
|
||||
|
||||
@@ -59,6 +60,7 @@ export default function RightRail() {
|
||||
|
||||
const { selectors } = useFileState();
|
||||
const { selectedFiles, selectedFileIds } = useFileSelection();
|
||||
const { actions: fileActions } = useFileActions();
|
||||
const { signaturesApplied } = useSignature();
|
||||
|
||||
const activeFiles = selectors.getFiles();
|
||||
@@ -141,7 +143,7 @@ export default function RightRail() {
|
||||
alert('You have unapplied signatures. Please use "Apply Signatures" first before exporting.');
|
||||
return;
|
||||
}
|
||||
viewerContext?.exportActions?.download();
|
||||
viewerContext?.exportActions?.download?.();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -150,23 +152,43 @@ export default function RightRail() {
|
||||
return;
|
||||
}
|
||||
|
||||
const filesToDownload = selectedFiles.length > 0 ? selectedFiles : activeFiles;
|
||||
filesToDownload.forEach(file => {
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(file);
|
||||
link.download = file.name;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(link.href);
|
||||
});
|
||||
const filesToExport = selectedFiles.length > 0 ? selectedFiles : activeFiles;
|
||||
const stubsToExport = selectedFiles.length > 0
|
||||
? selectors.getSelectedStirlingFileStubs()
|
||||
: selectors.getStirlingFileStubs();
|
||||
|
||||
if (filesToExport.length > 0) {
|
||||
for (let i = 0; i < filesToExport.length; i++) {
|
||||
const file = filesToExport[i];
|
||||
const stub = stubsToExport[i];
|
||||
console.log('[RightRail] Exporting file:', { fileName: file.name, stubId: stub?.id, localFilePath: stub?.localFilePath, isDirty: stub?.isDirty });
|
||||
const result = await downloadFile({
|
||||
data: file,
|
||||
filename: file.name,
|
||||
localPath: stub?.localFilePath
|
||||
});
|
||||
console.log('[RightRail] Export complete, checking dirty state:', { localFilePath: stub?.localFilePath, isDirty: stub?.isDirty, savedPath: result.savedPath });
|
||||
// Mark file as clean after successful save to disk
|
||||
if (stub && result.savedPath) {
|
||||
console.log('[RightRail] Marking file as clean:', stub.id);
|
||||
fileActions.updateStirlingFileStub(stub.id, {
|
||||
localFilePath: stub.localFilePath ?? result.savedPath,
|
||||
isDirty: false
|
||||
});
|
||||
} else {
|
||||
console.log('[RightRail] Skipping clean mark:', { savedPath: result.savedPath, isDirty: stub?.isDirty });
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [
|
||||
currentView,
|
||||
selectedFiles,
|
||||
activeFiles,
|
||||
pageEditorFunctions,
|
||||
viewerContext,
|
||||
signaturesApplied
|
||||
signaturesApplied,
|
||||
selectors,
|
||||
fileActions,
|
||||
]);
|
||||
|
||||
const downloadTooltip = useMemo(() => {
|
||||
|
||||
Reference in New Issue
Block a user