diff --git a/editor/src/components/editor/store/dispatch-strategies.tsx b/editor/src/components/editor/store/dispatch-strategies.tsx index 67f2d990e5ca..ea4ed8b30e40 100644 --- a/editor/src/components/editor/store/dispatch-strategies.tsx +++ b/editor/src/components/editor/store/dispatch-strategies.tsx @@ -654,12 +654,24 @@ export function handleStrategies( if (MeasureDispatchTime) { window.performance.mark('strategies_begin') } - const { unpatchedEditorState, patchedEditorState, newStrategyState } = handleStrategiesInner( - strategies, - dispatchedActions, - storedState, - result, - ) + let unpatchedEditorState: EditorState + let patchedEditorState: EditorState + let newStrategyState: StrategyState + if (storedState.projectServerState.isMyProject === 'no') { + unpatchedEditorState = result.unpatchedEditor + patchedEditorState = result.unpatchedEditor + newStrategyState = result.strategyState + } else { + const strategiesResult = handleStrategiesInner( + strategies, + dispatchedActions, + storedState, + result, + ) + unpatchedEditorState = strategiesResult.unpatchedEditorState + patchedEditorState = strategiesResult.patchedEditorState + newStrategyState = strategiesResult.newStrategyState + } const patchedEditorWithMetadata: EditorState = { ...patchedEditorState, diff --git a/editor/src/components/editor/store/dispatch.tsx b/editor/src/components/editor/store/dispatch.tsx index aa4e66a7a269..e7a3ac5fd93d 100644 --- a/editor/src/components/editor/store/dispatch.tsx +++ b/editor/src/components/editor/store/dispatch.tsx @@ -105,168 +105,195 @@ function processAction( action: EditorAction, spyCollector: UiJsxCanvasContextData, ): EditorStoreUnpatched { + let shouldProcessAction: boolean = true + // By default when the current user does not own the project and the action is non-transient prevent actions from running... + if (editorStoreUnpatched.projectServerState.isMyProject === 'no' && !isTransientAction(action)) { + // ...Unless they're more critical to the running of the editor in this case. + const allowedNonOwnerAction = + action.action === 'UPDATE_FROM_WORKER' || + action.action === 'UPDATE_TOP_LEVEL_ELEMENTS_FROM_COLLABORATION_UPDATE' + shouldProcessAction = allowedNonOwnerAction + } + // When the current user does own the project... + if (editorStoreUnpatched.projectServerState.isMyProject === 'yes') { + // ...Disallow these updates as they're coming from non-owners. + shouldProcessAction = action.action !== 'UPDATE_TOP_LEVEL_ELEMENTS_FROM_COLLABORATION_UPDATE' + } + let working = editorStoreUnpatched - // Sidestep around the local actions so that we definitely run them locally. - if (action.action === 'TRANSIENT_ACTIONS') { - // Drill into the array. - return processActions(dispatchEvent, working, action.transientActions, spyCollector) - } else if (action.action === 'ATOMIC' || action.action === 'MERGE_WITH_PREV_UNDO') { - // Drill into the array. - return processActions(dispatchEvent, working, action.actions, spyCollector) - } else if (action.action === 'UNDO' && !History.canUndo(working.history)) { - // Bail early and make no changes. - return processActions( - dispatchEvent, - working, - [ - EditorActions.addToast( - notice( - `Can't undo, reached the end of the undo history.`, - 'NOTICE', - false, - cannotUndoRedoToastId, + if (shouldProcessAction) { + // Sidestep around the local actions so that we definitely run them locally. + if (action.action === 'TRANSIENT_ACTIONS') { + // Drill into the array. + return processActions(dispatchEvent, working, action.transientActions, spyCollector) + } else if (action.action === 'ATOMIC' || action.action === 'MERGE_WITH_PREV_UNDO') { + // Drill into the array. + return processActions(dispatchEvent, working, action.actions, spyCollector) + } else if (action.action === 'UNDO' && !History.canUndo(working.history)) { + // Bail early and make no changes. + return processActions( + dispatchEvent, + working, + [ + EditorActions.addToast( + notice( + `Can't undo, reached the end of the undo history.`, + 'NOTICE', + false, + cannotUndoRedoToastId, + ), ), - ), - ], - spyCollector, - ) - } else if (action.action === 'REDO' && !History.canRedo(working.history)) { - // Bail early and make no changes. - return processActions( - dispatchEvent, - working, - [ - EditorActions.addToast( - notice( - `Can't redo, reached the end of the undo history.`, - 'NOTICE', - false, - cannotUndoRedoToastId, + ], + spyCollector, + ) + } else if (action.action === 'REDO' && !History.canRedo(working.history)) { + // Bail early and make no changes. + return processActions( + dispatchEvent, + working, + [ + EditorActions.addToast( + notice( + `Can't redo, reached the end of the undo history.`, + 'NOTICE', + false, + cannotUndoRedoToastId, + ), ), - ), - ], - spyCollector, - ) - } else if (action.action === 'SET_SHORTCUT') { - return { - ...working, - userState: UPDATE_FNS.SET_SHORTCUT(action, working.userState), - } - } else if (action.action === 'SET_CURRENT_THEME') { - return { - ...working, - userState: UPDATE_FNS.SET_CURRENT_THEME(action, working.userState), - } - } else if (action.action === 'SET_LOGIN_STATE') { - return { - ...working, - userState: UPDATE_FNS.SET_LOGIN_STATE(action, working.userState), - } - } else if (action.action === 'SET_GITHUB_STATE') { - return { - ...working, - userState: UPDATE_FNS.SET_GITHUB_STATE(action, working.userState), - } - } else if (action.action === 'SET_USER_CONFIGURATION') { - return { - ...working, - userState: UPDATE_FNS.SET_USER_CONFIGURATION(action, working.userState), + ], + spyCollector, + ) + } else if (action.action === 'SET_SHORTCUT') { + return { + ...working, + userState: UPDATE_FNS.SET_SHORTCUT(action, working.userState), + } + } else if (action.action === 'SET_CURRENT_THEME') { + return { + ...working, + userState: UPDATE_FNS.SET_CURRENT_THEME(action, working.userState), + } + } else if (action.action === 'SET_LOGIN_STATE') { + return { + ...working, + userState: UPDATE_FNS.SET_LOGIN_STATE(action, working.userState), + } + } else if (action.action === 'SET_GITHUB_STATE') { + return { + ...working, + userState: UPDATE_FNS.SET_GITHUB_STATE(action, working.userState), + } + } else if (action.action === 'SET_USER_CONFIGURATION') { + return { + ...working, + userState: UPDATE_FNS.SET_USER_CONFIGURATION(action, working.userState), + } } - } - if (action.action === 'UPDATE_TEXT') { - working = UPDATE_FNS.UPDATE_TEXT(action, working) - } + if (action.action === 'UPDATE_TEXT') { + working = UPDATE_FNS.UPDATE_TEXT(action, working) + } - if (action.action === 'TRUNCATE_HISTORY') { - working = UPDATE_FNS.TRUNCATE_HISTORY(working) - } + if (action.action === 'TRUNCATE_HISTORY') { + working = UPDATE_FNS.TRUNCATE_HISTORY(working) + } - if (action.action === 'START_POST_ACTION_SESSION') { - working = runExecuteStartPostActionMenuAction(action, working) - } + if (action.action === 'START_POST_ACTION_SESSION') { + working = runExecuteStartPostActionMenuAction(action, working) + } - if (action.action === 'EXECUTE_POST_ACTION_MENU_CHOICE') { - working = runExecuteWithPostActionMenuAction(action, working) - } + if (action.action === 'EXECUTE_POST_ACTION_MENU_CHOICE') { + working = runExecuteWithPostActionMenuAction(action, working) + } - if (action.action === 'CLEAR_POST_ACTION_SESSION') { - working = runClearPostActionSession(working) - } + if (action.action === 'CLEAR_POST_ACTION_SESSION') { + working = runClearPostActionSession(working) + } - if (action.action === 'UPDATE_PROJECT_SERVER_STATE') { - working = runUpdateProjectServerState(working, action) - } + if (action.action === 'UPDATE_PROJECT_SERVER_STATE') { + working = runUpdateProjectServerState(working, action) + } - // Process action on the JS side. - const editorAfterUpdateFunction = runLocalEditorAction( - working.unpatchedEditor, - working.unpatchedDerived, - working.userState, - working.workers, - action as EditorAction, - working.history, - dispatchEvent, - spyCollector, - working.builtInDependencies, - working.collaborativeEditingSupport, - working.projectServerState, - ) - const editorAfterCanvas = runLocalCanvasAction( - dispatchEvent, - editorAfterUpdateFunction, - working.unpatchedDerived, - working.builtInDependencies, - action as CanvasAction, - ) - const editorAfterNavigator = runLocalNavigatorAction( - editorAfterCanvas, - working.unpatchedDerived, - action as LocalNavigatorAction, - ) - const withPossiblyClearedPseudoInsert = maybeClearPseudoInsertMode( - editorStoreUnpatched.unpatchedEditor, - editorAfterNavigator, - action, - ) + // Process action on the JS side. + const editorAfterUpdateFunction = runLocalEditorAction( + working.unpatchedEditor, + working.unpatchedDerived, + working.userState, + working.workers, + action as EditorAction, + working.history, + dispatchEvent, + spyCollector, + working.builtInDependencies, + working.collaborativeEditingSupport, + working.projectServerState, + ) + const editorAfterCanvas = runLocalCanvasAction( + dispatchEvent, + editorAfterUpdateFunction, + working.unpatchedDerived, + working.builtInDependencies, + action as CanvasAction, + ) + const editorAfterNavigator = runLocalNavigatorAction( + editorAfterCanvas, + working.unpatchedDerived, + action as LocalNavigatorAction, + ) + const withPossiblyClearedPseudoInsert = maybeClearPseudoInsertMode( + editorStoreUnpatched.unpatchedEditor, + editorAfterNavigator, + action, + ) - let newStateHistory: StateHistory - switch (action.action) { - case 'UNDO': - newStateHistory = History.undo(working.unpatchedEditor.id, working.history, 'no-side-effects') - working.postActionInteractionSession = null - break - case 'REDO': - newStateHistory = History.redo(working.unpatchedEditor.id, working.history, 'no-side-effects') - break - case 'NEW': - case 'LOAD': - const derivedState = deriveState( - withPossiblyClearedPseudoInsert, - null, - 'unpatched', - unpatchedCreateRemixDerivedDataMemo, - ) - newStateHistory = History.init(withPossiblyClearedPseudoInsert, derivedState) - break - default: - newStateHistory = working.history - break - } + let newStateHistory: StateHistory + switch (action.action) { + case 'UNDO': + newStateHistory = History.undo( + working.unpatchedEditor.id, + working.history, + 'no-side-effects', + ) + working.postActionInteractionSession = null + break + case 'REDO': + newStateHistory = History.redo( + working.unpatchedEditor.id, + working.history, + 'no-side-effects', + ) + break + case 'NEW': + case 'LOAD': + const derivedState = deriveState( + withPossiblyClearedPseudoInsert, + null, + 'unpatched', + unpatchedCreateRemixDerivedDataMemo, + ) + newStateHistory = History.init(withPossiblyClearedPseudoInsert, derivedState) + break + default: + newStateHistory = working.history + break + } - return { - unpatchedEditor: withPossiblyClearedPseudoInsert, - unpatchedDerived: working.unpatchedDerived, - strategyState: working.strategyState, // this means the actions cannot update strategyState – this piece of state lives outside our "redux" state - postActionInteractionSession: working.postActionInteractionSession, - history: newStateHistory, - userState: working.userState, - workers: working.workers, - persistence: working.persistence, - saveCountThisSession: working.saveCountThisSession, - builtInDependencies: working.builtInDependencies, - projectServerState: working.projectServerState, - collaborativeEditingSupport: working.collaborativeEditingSupport, + return { + unpatchedEditor: withPossiblyClearedPseudoInsert, + unpatchedDerived: working.unpatchedDerived, + strategyState: working.strategyState, // this means the actions cannot update strategyState – this piece of state lives outside our "redux" state + postActionInteractionSession: working.postActionInteractionSession, + history: newStateHistory, + userState: working.userState, + workers: working.workers, + persistence: working.persistence, + saveCountThisSession: working.saveCountThisSession, + builtInDependencies: working.builtInDependencies, + projectServerState: working.projectServerState, + collaborativeEditingSupport: working.collaborativeEditingSupport, + } + } else { + return working } }