Skip to content

Commit

Permalink
feature(collaboration) Handle imports and exports correctly in collab…
Browse files Browse the repository at this point in the history
…oration. (#4565)

- Added `UPDATE_EXPORTS_DETAIL_FROM_COLLABORATION_UPDATE` and
  `UPDATE_IMPORTS_FROM_COLLABORATION_UPDATE` editor actions.
- `addHookForProjectChanges` now collates potentially many actions to
  dispatch at once.
- Reworked `ArrayChanges` as it didn't work very well in its original form.
- Refactored `updateEntireProjectContents` to produce a multitude of
  editor actions and dispatch all at once.
- Created `updateEditorWithArrayChanges` and `updateEditorWithMapChanges`
  utility functions.
- Implemented `updateTopLevelElementsOfFile`, `updateExportsDetailOfFile`
  and `updateImportsOfFile` to carry changes from the yjs types into the
  editor.
- `calculateArrayChanges` now produces step by step changes.
- Implemented `syncArrayChanges` and `syncMapChanges` utility functions.
- `synchroniseParseSuccesToCollabFile` now much more simplified than the
  original implementations.
- Reworked `updateEntireProjectContents` to handle key changes
  instead of using the `currentTarget` value so that file deletions
  are handled correctly.
- Strip source maps from the outgoing top level elements. (#4566)
  • Loading branch information
seanparsons authored Nov 27, 2023
1 parent 23efa1c commit 2e733e3
Show file tree
Hide file tree
Showing 8 changed files with 452 additions and 96 deletions.
17 changes: 17 additions & 0 deletions editor/src/components/editor/action-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import type {
Imports,
ParsedTextFile,
ImageFile,
ExportDetail,
ImportDetails,
} from '../../core/shared/project-file-types'
import { StaticElementPathPart } from '../../core/shared/project-file-types'
import type { CodeResultCache, PropertyControlsInfo } from '../custom-code/code-file'
Expand Down Expand Up @@ -79,6 +81,7 @@ import type { PostActionChoice } from '../canvas/canvas-strategies/post-action-o
import type { FromVSCodeAction } from './actions/actions-from-vscode'
import type { ProjectServerState } from './store/project-server-state'
import type { SetHuggingParentToFixed } from '../canvas/canvas-strategies/strategies/convert-to-absolute-and-move-strategy'
import type { MapLike } from 'typescript'
export { isLoggedIn, loggedInUser, notLoggedIn } from '../../common/user'
export type { LoginState, UserDetails } from '../../common/user'

Expand Down Expand Up @@ -1062,6 +1065,18 @@ export interface UpdateTopLevelElementsFromCollaborationUpdate {
topLevelElements: Array<TopLevelElement>
}

export interface UpdateExportsDetailFromCollaborationUpdate {
action: 'UPDATE_EXPORTS_DETAIL_FROM_COLLABORATION_UPDATE'
fullPath: string
exportsDetail: Array<ExportDetail>
}

export interface UpdateImportsFromCollaborationUpdate {
action: 'UPDATE_IMPORTS_FROM_COLLABORATION_UPDATE'
fullPath: string
imports: MapLike<ImportDetails>
}

export type EditorAction =
| ClearSelection
| InsertJSXElement
Expand Down Expand Up @@ -1234,6 +1249,8 @@ export type EditorAction =
| TruncateHistory
| UpdateProjectServerState
| UpdateTopLevelElementsFromCollaborationUpdate
| UpdateExportsDetailFromCollaborationUpdate
| UpdateImportsFromCollaborationUpdate

export type DispatchPriority =
| 'everyone'
Expand Down
25 changes: 25 additions & 0 deletions editor/src/components/editor/actions/action-creators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import type {
StaticElementPathPart,
ElementPath,
ImageFile,
ExportDetail,
} from '../../../core/shared/project-file-types'
import type { BuildType } from '../../../core/workers/common/worker-types'
import type { Key, KeysPressed } from '../../../utils/keyboard'
Expand Down Expand Up @@ -222,6 +223,8 @@ import type {
UpdateProjectServerState,
UpdateTopLevelElementsFromCollaborationUpdate,
DeleteFileFromCollaboration,
UpdateExportsDetailFromCollaborationUpdate,
UpdateImportsFromCollaborationUpdate,
} from '../action-types'
import type { InsertionSubjectWrapper, Mode } from '../editor-modes'
import { EditorModes, insertionSubject } from '../editor-modes'
Expand Down Expand Up @@ -1682,3 +1685,25 @@ export function updateTopLevelElementsFromCollaborationUpdate(
topLevelElements: topLevelElements,
}
}

export function updateExportsDetailFromCollaborationUpdate(
fullPath: string,
exportsDetail: Array<ExportDetail>,
): UpdateExportsDetailFromCollaborationUpdate {
return {
action: 'UPDATE_EXPORTS_DETAIL_FROM_COLLABORATION_UPDATE',
fullPath: fullPath,
exportsDetail: exportsDetail,
}
}

export function updateImportsFromCollaborationUpdate(
fullPath: string,
imports: Imports,
): UpdateImportsFromCollaborationUpdate {
return {
action: 'UPDATE_IMPORTS_FROM_COLLABORATION_UPDATE',
fullPath: fullPath,
imports: imports,
}
}
2 changes: 2 additions & 0 deletions editor/src/components/editor/actions/action-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ export function isTransientAction(action: EditorAction): boolean {
case 'UPDATE_CONIDTIONAL_EXPRESSION':
case 'CUT_SELECTION_TO_CLIPBOARD':
case 'UPDATE_TOP_LEVEL_ELEMENTS_FROM_COLLABORATION_UPDATE':
case 'UPDATE_EXPORTS_DETAIL_FROM_COLLABORATION_UPDATE':
case 'UPDATE_IMPORTS_FROM_COLLABORATION_UPDATE':
return false
case 'SAVE_ASSET':
return (
Expand Down
124 changes: 123 additions & 1 deletion editor/src/components/editor/actions/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ import type {
ScrollToPosition,
UpdateTopLevelElementsFromCollaborationUpdate,
DeleteFileFromCollaboration,
UpdateExportsDetailFromCollaborationUpdate,
UpdateImportsFromCollaborationUpdate,
} from '../action-types'
import { isLoggedIn } from '../action-types'
import type { Mode } from '../editor-modes'
Expand Down Expand Up @@ -424,6 +426,8 @@ import {
} from '../../../core/vscode/vscode-bridge'
import { createClipboardDataFromSelection, Clipboard } from '../../../utils/clipboard'
import {
ExportDetailKeepDeepEquality,
ImportDetailsKeepDeepEquality,
NavigatorStateKeepDeepEquality,
TopLevelElementKeepDeepEquality,
} from '../store/store-deep-equality-instances'
Expand Down Expand Up @@ -543,7 +547,7 @@ import { resultForFirstApplicableStrategy } from '../../inspector/inspector-stra
import { reparentToUnwrapAsAbsoluteStrategy } from '../one-shot-unwrap-strategies/reparent-to-unwrap-as-absolute-strategy'
import { convertToAbsoluteAndReparentToUnwrapStrategy } from '../one-shot-unwrap-strategies/convert-to-absolute-and-reparent-to-unwrap'
import { addHookForProjectChanges } from '../store/collaborative-editing'
import { arrayDeepEquality } from '../../../utils/deep-equality'
import { arrayDeepEquality, objectDeepEquality } from '../../../utils/deep-equality'
import type { ProjectServerState } from '../store/project-server-state'
import { fixParseSuccessUIDs } from '../../../core/workers/parser-printer/uid-fix'

Expand Down Expand Up @@ -5395,6 +5399,124 @@ export const UPDATE_FNS = {
}
}

return {
...updatedEditor,
filesModifiedByAnotherUser: updatedEditor.filesModifiedByAnotherUser.concat(
action.fullPath,
),
}
}
},
UPDATE_EXPORTS_DETAIL_FROM_COLLABORATION_UPDATE: (
action: UpdateExportsDetailFromCollaborationUpdate,
editor: EditorModel,
serverState: ProjectServerState,
): EditorModel => {
// When the current user does own the project...
if (serverState.isMyProject === 'yes') {
// ...Disallow these updates as they're coming from non-owners.
return editor
} else {
let updatedEditor: EditorModel = editor
if (fileExists(editor.projectContents, action.fullPath)) {
updatedEditor = modifyParseSuccessAtPath(
action.fullPath,
editor,
(parsed) => {
const newExportsDetailsDeepEquals = arrayDeepEquality(ExportDetailKeepDeepEquality)(
parsed.exportsDetail,
action.exportsDetail,
)

if (newExportsDetailsDeepEquals.areEqual) {
return parsed
} else {
return {
...parsed,
exportsDetail: newExportsDetailsDeepEquals.value,
}
}
},
false,
)
} else {
const newParseSuccess = parseSuccess({}, [], {}, null, null, action.exportsDetail, {})
const newFile = textFile(
textFileContents('', newParseSuccess, RevisionsState.ParsedAhead),
null,
null,
1,
)
const updatedProjectContents = addFileToProjectContents(
editor.projectContents,
action.fullPath,
newFile,
)
updatedEditor = {
...editor,
projectContents: updatedProjectContents,
}
}

return {
...updatedEditor,
filesModifiedByAnotherUser: updatedEditor.filesModifiedByAnotherUser.concat(
action.fullPath,
),
}
}
},
UPDATE_IMPORTS_FROM_COLLABORATION_UPDATE: (
action: UpdateImportsFromCollaborationUpdate,
editor: EditorModel,
serverState: ProjectServerState,
): EditorModel => {
// When the current user does own the project...
if (serverState.isMyProject === 'yes') {
// ...Disallow these updates as they're coming from non-owners.
return editor
} else {
let updatedEditor: EditorModel = editor
if (fileExists(editor.projectContents, action.fullPath)) {
updatedEditor = modifyParseSuccessAtPath(
action.fullPath,
editor,
(parsed) => {
const newImportsDeepEquals = objectDeepEquality(ImportDetailsKeepDeepEquality)(
parsed.imports,
action.imports,
)

if (newImportsDeepEquals.areEqual) {
return parsed
} else {
return {
...parsed,
imports: newImportsDeepEquals.value,
}
}
},
false,
)
} else {
const newParseSuccess = parseSuccess(action.imports, [], {}, null, null, [], {})
const newFile = textFile(
textFileContents('', newParseSuccess, RevisionsState.ParsedAhead),
null,
null,
1,
)
const updatedProjectContents = addFileToProjectContents(
editor.projectContents,
action.fullPath,
newFile,
)
updatedEditor = {
...editor,
projectContents: updatedProjectContents,
}
}

return {
...updatedEditor,
filesModifiedByAnotherUser: updatedEditor.filesModifiedByAnotherUser.concat(
Expand Down
Loading

0 comments on commit 2e733e3

Please sign in to comment.