diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XY-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XY-1-Google-Chrome-linux.png index 18b0f99dc..d2109c32c 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XY-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XY-1-Google-Chrome-linux.png differ diff --git a/src/components/FileMachineProvider.tsx b/src/components/FileMachineProvider.tsx index 59c5ba45c..b72027df7 100644 --- a/src/components/FileMachineProvider.tsx +++ b/src/components/FileMachineProvider.tsx @@ -34,6 +34,7 @@ import { settingsActor, useSettings } from 'machines/appMachine' import { createRouteCommands } from 'lib/commandBarConfigs/routeCommandConfig' import { useToken } from 'machines/appMachine' import { createNamedViewsCommand } from 'lib/commandBarConfigs/namedViewsConfig' +import { reportRejection } from 'lib/trap' type MachineContext = { state: StateFrom @@ -60,6 +61,20 @@ export const FileMachineProvider = ({ [] ) + // Write code mirror content to disk when the page is trying to reroute + // Our logic for codeManager.writeToFile has an artificial 1000ms timeout which + // won't run quickly enough so users can make an edit, exit the page and lose their + // progress within that 1000ms window. + useEffect(() => { + const preventUnload = (event: BeforeUnloadEvent) => { + codeManager.writeToFileNoTimeout().catch(reportRejection) + } + window.addEventListener('beforeunload', preventUnload) + return () => { + window.removeEventListener('beforeunload', preventUnload) + } + }, []) + useEffect(() => { // TODO: Engine feature is not deployed if (DEV) { diff --git a/src/lang/codeManager.ts b/src/lang/codeManager.ts index 4a36dac80..8dda76756 100644 --- a/src/lang/codeManager.ts +++ b/src/lang/codeManager.ts @@ -164,6 +164,32 @@ export default class CodeManager { } } + // When we unload the page via changing routes we want to instantly write to disk to save their progress + // There is a race condition in the system. writeToFile takes 1000ms to run, if they make an edit and leave within the 1000ms + // window they won't get their content saved. Use this to always save their file before rerouting + async writeToFileNoTimeout() { + if (isDesktop()) { + return new Promise((resolve, reject) => { + if (!this._currentFilePath) + return reject(new Error('currentFilePath not set')) + + // Wait one event loop to give a chance for params to be set + // Save the file to disk + window.electron + .writeFile(this._currentFilePath, this.code ?? '') + .then(resolve) + .catch((err: Error) => { + // TODO: add tracing per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254) + console.error('error saving file', err) + toast.error('Error saving file, please check file permissions') + reject(err) + }) + }) + } else { + safeLSSetItem(PERSIST_CODE_KEY, this.code) + } + } + async updateEditorWithAstAndWriteToFile(ast: Program) { // We clear the AST when there it cannot be parsed, so if we are trying to write an empty AST, its // probably because of an earlier error. That's a bad state to be in and it's not going to be