Skip to content
This repository has been archived by the owner on Feb 25, 2024. It is now read-only.

Refactored to use XState typegen #341

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@supabase/supabase-js": "^1.22.5",
"@xstate/graph": "^1.3.0",
"@xstate/inspect": "^0.4.1",
"@xstate/react": "^1.5.1",
"@xstate/react": "^2.0.0",
"date-fns": "^2.22.1",
"elkjs": "^0.7.1",
"framer-motion": "^4",
Expand All @@ -30,7 +30,7 @@
"realms-shim": "^1.2.2",
"web-vitals": "^1.0.1",
"web-worker": "^1.0.0",
"xstate": "^4.26.0"
"xstate": "^4.29.0"
},
"scripts": {
"start": "concurrently \"npm:start:app\" \"npm:graphql:codegen\"",
Expand Down
3 changes: 2 additions & 1 deletion src/CanvasContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const dragModel = createModel(

const dragMachine = dragModel.createMachine(
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsTypes: {} as import("./CanvasContainer.typegen").Typegen0,
preserveActionOrder: true,
initial: 'checking_if_disabled',
states: {
Expand Down Expand Up @@ -282,7 +283,7 @@ export const CanvasContainer: React.FC<{ panModeEnabled: boolean }> = ({
const [state, send] = useMachine(dragMachine, {
actions: {
sendPanChange: actions.send(
(_, ev: any) => {
(_, ev) => {
// we need to translate a pointer move to the viewbox move
// and that is going into the opposite direction than the pointer
return canvasModel.events.PAN(-ev.delta.x, -ev.delta.y);
Expand Down
70 changes: 70 additions & 0 deletions src/CanvasContainer.typegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// This file was automatically generated. Edits will be overwritten

export interface Typegen0 {
'@@xstate/typegen': true;
eventsCausingActions: {
sendPanChange: 'POINTER_MOVED_BY';
enableTextSelection: string;
disableTextSelection: 'ENABLE_PANNING';
};
internalEvents: {};
invokeSrcNameMap: {
invokeDetectLock: 'done.invoke.(machine).enabled.mode.lockable.released:invocation[0]';
wheelPressListener: 'done.invoke.(machine).enabled.mode.lockable.released:invocation[1]';
invokeDetectRelease: 'done.invoke.(machine).enabled.mode.lockable.locked:invocation[0]';
};
missingImplementations: {
actions: 'sendPanChange';
services: never;
guards: 'isPanDisabled';
delays: never;
};
eventsCausingServices: {
invokeDetectLock: 'RELEASE' | 'DRAG_SESSION_STOPPED';
wheelPressListener: 'RELEASE' | 'DRAG_SESSION_STOPPED';
invokeDetectRelease: 'LOCK';
};
eventsCausingGuards: {
isPanDisabled: string;
};
eventsCausingDelays: {};
matchesStates:
| 'checking_if_disabled'
| 'permanently_disabled'
| 'enabled'
| 'enabled.mode'
| 'enabled.mode.lockable'
| 'enabled.mode.lockable.released'
| 'enabled.mode.lockable.locked'
| 'enabled.mode.lockable.wheelPressed'
| 'enabled.mode.pan'
| 'enabled.panning'
| 'enabled.panning.disabled'
| 'enabled.panning.enabled'
| 'enabled.panning.enabled.idle'
| 'enabled.panning.enabled.active'
| 'enabled.panning.enabled.active.grabbed'
| 'enabled.panning.enabled.active.dragging'
| 'enabled.panning.enabled.active.done'
| {
enabled?:
| 'mode'
| 'panning'
| {
mode?:
| 'lockable'
| 'pan'
| { lockable?: 'released' | 'locked' | 'wheelPressed' };
panning?:
| 'disabled'
| 'enabled'
| {
enabled?:
| 'idle'
| 'active'
| { active?: 'grabbed' | 'dragging' | 'done' };
};
};
};
tags: never;
}
112 changes: 62 additions & 50 deletions src/EditorPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ import { useActor, useMachine, useSelector } from '@xstate/react';
import { editor, Range } from 'monaco-editor';
import dynamic from 'next/dynamic';
import React from 'react';
import { ActorRefFrom, assign, DoneInvokeEvent, send, spawn } from 'xstate';
import {
ActorRefFrom,
assign,
DoneInvokeEvent,
send,
spawn,
StateNode,
} from 'xstate';
import { createModel } from 'xstate/lib/model';
import { useAuth } from './authContext';
import { CommandPalette } from './CommandPalette';
Expand Down Expand Up @@ -83,9 +90,9 @@ const editorPanelModel = createModel(
notifRef: undefined! as ActorRefFrom<typeof notifMachine>,
monacoRef: null as Monaco | null,
standaloneEditorRef: null as editor.IStandaloneCodeEditor | null,
sourceRef: null as SourceMachineActorRef,
sourceRef: null as unknown as SourceMachineActorRef,
mainFile: 'main.ts',
machines: null as AnyStateMachine[] | null,
machines: null as ReturnType<typeof parseMachines> | null,
deltaDecorations: [] as string[],
},
{
Expand All @@ -107,6 +114,14 @@ const editorPanelModel = createModel(

const editorPanelMachine = editorPanelModel.createMachine(
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsTypes: {} as import("./EditorPanel.typegen").Typegen0,
schema: {} as {
services: {
parseMachines: {
data: ReturnType<typeof parseMachines>;
};
};
},
entry: [assign({ notifRef: () => spawn(notifMachine) })],
initial: 'booting',
states: {
Expand Down Expand Up @@ -185,48 +200,10 @@ const editorPanelMachine = editorPanelModel.createMachine(
compiling: {
tags: ['visualizing'],
invoke: {
src: async (ctx) => {
const monaco = ctx.monacoRef!;
const uri = monaco.Uri.parse(ctx.mainFile);
const tsWoker = await monaco.languages.typescript
.getTypeScriptWorker()
.then((worker) => worker(uri));

const syntaxErrors = await tsWoker.getSyntacticDiagnostics(
uri.toString(),
);

if (syntaxErrors.length > 0) {
const model = ctx.monacoRef?.editor.getModel(uri);
// Only report one error at a time
const error = syntaxErrors[0];

const start = model?.getPositionAt(error.start!);
const end = model?.getPositionAt(error.start! + error.length!);
const errorRange = new ctx.monacoRef!.Range(
start?.lineNumber!,
0, // beginning of the line where error occured
end?.lineNumber!,
end?.column!,
);
return Promise.reject(
new SyntaxError(error.messageText.toString(), errorRange),
);
}

const compiledSource = await tsWoker
.getEmitOutput(uri.toString())
.then((result) => result.outputFiles[0].text);

return parseMachines(compiledSource);
},
src: 'parseMachines',
onDone: {
target: 'updating',
actions: [
assign({
machines: (_, e: any) => e.data,
}),
],
actions: ['assignParsedMachinesToContext'],
},
onError: [
{
Expand Down Expand Up @@ -276,13 +253,15 @@ const editorPanelMachine = editorPanelModel.createMachine(
guards: {
isGist: (ctx) =>
ctx.sourceRef.getSnapshot()!.context.sourceProvider === 'gist',
isSyntaxError: (_, e: any) => e.data instanceof SyntaxError,
isSyntaxError: (_, e) => e.data instanceof SyntaxError,
},
actions: {
broadcastError: send((_, e: any) => ({
assignParsedMachinesToContext: assign({
machines: (_, e) => e.data,
}),
broadcastError: send((_, e) => ({
type: 'EDITOR_ENCOUNTERED_ERROR',
title: e.data.title,
message: e.data.message,
message: (e.data as Error).message,
})),
addDecorations: assign({
deltaDecorations: (ctx, e) => {
Expand Down Expand Up @@ -322,6 +301,41 @@ const editorPanelMachine = editorPanelModel.createMachine(
editor?.revealLineInCenterIfOutsideViewport(range.startLineNumber);
},
},
services: {
parseMachines: async (ctx) => {
const monaco = ctx.monacoRef!;
const uri = monaco.Uri.parse(ctx.mainFile);
const tsWoker = await monaco.languages.typescript
.getTypeScriptWorker()
.then((worker) => worker(uri));

const syntaxErrors = await tsWoker.getSyntacticDiagnostics(
uri.toString(),
);

if (syntaxErrors.length > 0) {
const model = ctx.monacoRef?.editor.getModel(uri);
// Only report one error at a time
const error = syntaxErrors[0];

const start = model?.getPositionAt(error.start!);
const end = model?.getPositionAt(error.start! + error.length!);
const errorRange = new ctx.monacoRef!.Range(
start?.lineNumber!,
0, // beginning of the line where error occured
end?.lineNumber!,
end?.column!,
);
throw new SyntaxError(error.messageText.toString(), errorRange);
}

const compiledSource = await tsWoker
.getEmitOutput(uri.toString())
.then((result) => result.outputFiles[0].text);

return parseMachines(compiledSource);
},
},
},
);

Expand Down Expand Up @@ -373,7 +387,7 @@ export const EditorPanel: React.FC<{
onSave: () => void;
onFork: () => void;
onCreateNew: () => void;
onChange: (machine: AnyStateMachine[]) => void;
onChange: (machine: ReturnType<typeof parseMachines>) => void;
onChangedCodeValue: (code: string) => void;
}> = ({ onSave, onChange, onChangedCodeValue, onFork, onCreateNew }) => {
const embed = useEmbed();
Expand Down Expand Up @@ -512,8 +526,6 @@ export const EditorPanel: React.FC<{
{sourceOwnershipStatus === 'user-owns-source' &&
!embed?.isEmbedded && (
<Button
disabled={sourceState.hasTag('forking')}
isLoading={sourceState.hasTag('forking')}
onClick={() => {
onFork();
}}
Expand Down
58 changes: 58 additions & 0 deletions src/EditorPanel.typegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// This file was automatically generated. Edits will be overwritten

export interface Typegen0 {
'@@xstate/typegen': true;
eventsCausingActions: {
onChangedCodeValue: 'EDITOR_CHANGED_VALUE';
clearDecorations: 'EDITOR_CHANGED_VALUE';
onChange: 'UPDATE_MACHINE_PRESSED';
broadcastError:
| 'error.platform.(machine).booting.fixing_gist_imports:invocation[0]'
| 'error.platform.(machine).compiling:invocation[0]';
assignParsedMachinesToContext: 'done.invoke.(machine).compiling:invocation[0]';
addDecorations: 'error.platform.(machine).compiling:invocation[0]';
scrollToLineWithError: 'error.platform.(machine).compiling:invocation[0]';
};
internalEvents: {
'error.platform.(machine).booting.fixing_gist_imports:invocation[0]': {
type: 'error.platform.(machine).booting.fixing_gist_imports:invocation[0]';
data: unknown;
};
'error.platform.(machine).compiling:invocation[0]': {
type: 'error.platform.(machine).compiling:invocation[0]';
data: unknown;
};
'done.invoke.(machine).compiling:invocation[0]': {
type: 'done.invoke.(machine).compiling:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
};
invokeSrcNameMap: {
parseMachines: 'done.invoke.(machine).compiling:invocation[0]';
};
missingImplementations: {
actions: 'onChangedCodeValue' | 'onChange';
services: never;
guards: never;
delays: never;
};
eventsCausingServices: {
parseMachines: 'COMPILE' | 'done.state.(machine).booting';
};
eventsCausingGuards: {
isGist: 'EDITOR_READY';
isSyntaxError: 'error.platform.(machine).compiling:invocation[0]';
};
eventsCausingDelays: {};
matchesStates:
| 'booting'
| 'booting.waiting_for_monaco'
| 'booting.fixing_gist_imports'
| 'booting.done'
| 'active'
| 'updating'
| 'compiling'
| { booting?: 'waiting_for_monaco' | 'fixing_gist_imports' | 'done' };
tags: 'visualizing';
}
11 changes: 5 additions & 6 deletions src/EmbedPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ const extractFormData = (form: HTMLFormElement): ParsedEmbed => {
/**
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox
*/
const getEmbedCodeFromUrl = (embedUrl: string) => `<iframe src="${embedUrl}" sandbox="allow-same-origin allow-scripts"></iframe>`;
const getEmbedCodeFromUrl = (embedUrl: string) =>
`<iframe src="${embedUrl}" sandbox="allow-same-origin allow-scripts"></iframe>`;

const embedPreviewModel = createModel(
{
Expand Down Expand Up @@ -91,6 +92,7 @@ const embedPreviewModel = createModel(
);

const embedPreviewMachine = embedPreviewModel.createMachine({
tsTypes: {} as import("./EmbedPreview.typegen").Typegen0,
id: 'preview',
type: 'parallel',
preserveActionOrder: false, // TODO: remove this after we figured why this makes a bug
Expand All @@ -101,11 +103,7 @@ const embedPreviewMachine = embedPreviewModel.createMachine({
ready: {
entry: [
'makeEmbedUrlAndCode',
pure((ctx: ContextFrom<typeof embedPreviewModel>) => {
if (!ctx.loaded) {
return { type: 'makePreviewUrl' };
}
}),
'makePreviewUrl',
'updateEmbedCopy',
send('PREVIEW'),
],
Expand Down Expand Up @@ -220,6 +218,7 @@ const EmbedPreviewContent: React.FC = () => {
};
}),
makePreviewUrl: assign((ctx) => {
if (!ctx.loaded) return {};
const url = makeEmbedUrl(
router.query.sourceFileId as string,
window.location.origin,
Expand Down
Loading