fix(editor): sync URL after New workspace + sever project identity on .vlx import
Three stale-project-identity fixes from reviewing the New-workspace flow: - New workspace (web): handleNewClick cleared the workspace and the current project but left the browser on the old /user/slug URL — a refresh (or back-button pop) silently reloaded the OLD project over the fresh unsaved workspace. Now replaceState's to the localized /editor (replace, not push, so no back-entry points at the stale project route). - New workspace (desktop menu): same URL fix for the newProject menu action, which cleared identity but never left the project route. - .vlx import: importVlxFile mutated the stores WITHOUT clearing currentProject — with a saved project open, autosave saw the imported content as dirty edits on the old projectId and silently PUT the .vlx contents over the user's saved project (and pushed the clobber to GitHub on linked projects). Now severs identity first, same guard loadExample.ts already documents.
This commit is contained in:
parent
30882f3930
commit
8e33088752
|
|
@ -224,6 +224,22 @@ function newProject(): void {
|
||||||
|
|
||||||
// Clear the compile output panel so old build logs don't carry over.
|
// Clear the compile output panel so old build logs don't carry over.
|
||||||
compileLogs.clear();
|
compileLogs.clear();
|
||||||
|
|
||||||
|
// Leave whatever project URL we were on: staying there would reload the
|
||||||
|
// OLD project over this fresh workspace on refresh. replaceState (not
|
||||||
|
// navigateTo's pushState) so the back button can't pop to the stale
|
||||||
|
// project URL either; the popstate dispatch lets React Router render
|
||||||
|
// the plain editor route.
|
||||||
|
const cur = window.location.pathname;
|
||||||
|
const localeMatch = cur.match(/^\/([a-z]{2}(?:-[a-z]{2})?)\b/);
|
||||||
|
const prefix = localeMatch && LOCALES.includes(localeMatch[1] as Locale)
|
||||||
|
? `/${localeMatch[1]}`
|
||||||
|
: '';
|
||||||
|
const editorPath = `${prefix}/editor`;
|
||||||
|
if (cur !== editorPath) {
|
||||||
|
window.history.replaceState(null, '', editorPath);
|
||||||
|
window.dispatchEvent(new PopStateEvent('popstate'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkForUpdates(): Promise<void> {
|
async function checkForUpdates(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import React, { useRef, useState, useCallback, useEffect, lazy, Suspense } from
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { startSimulation } from '../simulation/spice/start';
|
import { startSimulation } from '../simulation/spice/start';
|
||||||
import { useSEO } from '../utils/useSEO';
|
import { useSEO } from '../utils/useSEO';
|
||||||
|
import { getLocaleFromPath, localizedPath } from '../i18n/path';
|
||||||
import { restoreStashedWorkspace } from '../utils/workspaceDraft';
|
import { restoreStashedWorkspace } from '../utils/workspaceDraft';
|
||||||
import { CodeEditor } from '../components/editor/CodeEditor';
|
import { CodeEditor } from '../components/editor/CodeEditor';
|
||||||
import { EditorToolbar } from '../components/editor/EditorToolbar';
|
import { EditorToolbar } from '../components/editor/EditorToolbar';
|
||||||
|
|
@ -219,6 +220,16 @@ export const EditorPage: React.FC = () => {
|
||||||
.getState()
|
.getState()
|
||||||
.addBoard('arduino-uno', DEFAULT_BOARD_POSITION.x, DEFAULT_BOARD_POSITION.y);
|
.addBoard('arduino-uno', DEFAULT_BOARD_POSITION.x, DEFAULT_BOARD_POSITION.y);
|
||||||
useSimulatorStore.getState().setActiveBoardId(newId);
|
useSimulatorStore.getState().setActiveBoardId(newId);
|
||||||
|
// The workspace no longer belongs to whatever project URL we were on —
|
||||||
|
// leaving it would silently reload the OLD project over this fresh
|
||||||
|
// workspace on refresh (and via the back button). replaceState, not
|
||||||
|
// pushState: a back-entry pointing at the stale project URL would
|
||||||
|
// remount the project route and cause exactly that reload.
|
||||||
|
const locale = getLocaleFromPath(window.location.pathname);
|
||||||
|
const editorPath = localizedPath('/editor', locale);
|
||||||
|
if (window.location.pathname !== editorPath) {
|
||||||
|
window.history.replaceState(null, '', editorPath);
|
||||||
|
}
|
||||||
}, [t]);
|
}, [t]);
|
||||||
|
|
||||||
// Track mobile breakpoint
|
// Track mobile breakpoint
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import type { Component } from '../types/component';
|
||||||
import type { Wire } from '../types/wire';
|
import type { Wire } from '../types/wire';
|
||||||
import { useEditorStore, chipFileGroupId } from '../store/useEditorStore';
|
import { useEditorStore, chipFileGroupId } from '../store/useEditorStore';
|
||||||
import { useSimulatorStore } from '../store/useSimulatorStore';
|
import { useSimulatorStore } from '../store/useSimulatorStore';
|
||||||
|
import { useProjectStore } from '../store/useProjectStore';
|
||||||
|
|
||||||
const VLX_FORMAT = 'velxio-project';
|
const VLX_FORMAT = 'velxio-project';
|
||||||
const VLX_VERSION = 1;
|
const VLX_VERSION = 1;
|
||||||
|
|
@ -228,6 +229,12 @@ export async function parseVlxFile(file: File): Promise<VlxPayload> {
|
||||||
*/
|
*/
|
||||||
export async function importVlxFile(file: File): Promise<VlxPayload> {
|
export async function importVlxFile(file: File): Promise<VlxPayload> {
|
||||||
const payload = await parseVlxFile(file);
|
const payload = await parseVlxFile(file);
|
||||||
|
// CRITICAL — sever the current project identity BEFORE mutating any store
|
||||||
|
// (same guard as loadExample.ts). With a saved project open, the auto-save
|
||||||
|
// hook would otherwise see the imported content as dirty edits on the OLD
|
||||||
|
// projectId and silently PUT the .vlx contents over the user's saved
|
||||||
|
// project (and push the clobber to GitHub when the project is linked).
|
||||||
|
useProjectStore.getState().clearCurrentProject();
|
||||||
useSimulatorStore.getState().loadProjectState({
|
useSimulatorStore.getState().loadProjectState({
|
||||||
boards: payload.boards as unknown as BoardInstance[],
|
boards: payload.boards as unknown as BoardInstance[],
|
||||||
fileGroups: payload.fileGroups,
|
fileGroups: payload.fileGroups,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue