Skip to content

Commit b3e49da

Browse files
authored
Merge pull request #1894 from processing/chore/sandbox
Serve sketch preview from subdomain
2 parents 691a6e9 + 14513b9 commit b3e49da

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1917
-1265
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ ML5_LIBRARY_PASS=helloml5
2222
MOBILE_ENABLED=true
2323
MONGO_URL=mongodb://localhost:27017/p5js-web-editor
2424
PORT=8000
25+
PREVIEW_PORT=8002
26+
EDITOR_URL=http://localhost:8000
27+
PREVIEW_URL=http://localhost:8002
2528
S3_BUCKET=<your-s3-bucket>
2629
S3_BUCKET_URL_BASE=<alt-for-s3-url>
2730
SESSION_SECRET=whatever_you_want_this_to_be_it_only_matters_for_production

client/index.integration.test.jsx

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ describe('index.jsx integration', () => {
139139
expect(
140140
screen.getByRole('heading', { name: /preview/i })
141141
).toBeInTheDocument();
142-
const preview = screen.getByRole('main', { name: /sketch output/i });
142+
const preview = screen.getByTitle(/sketch preview/i);
143143
expect(preview).toBeInTheDocument();
144144
});
145145

@@ -152,24 +152,27 @@ describe('index.jsx integration', () => {
152152
expect(screen.getByText('Sketch Files')).toBeInTheDocument();
153153
});
154154

155-
it('clicking on play updates the preview iframe with a srcdoc, stop clears it', () => {
156-
const playButton = screen.getByRole('button', {
157-
name: /play only visual sketch/i
158-
});
159-
const preview = screen.getByRole('main', { name: /sketch output/i });
160-
expect(preview.getAttribute('srcdoc')).toBeFalsy();
161-
act(() => {
162-
fireEvent.click(playButton);
163-
});
164-
165-
expect(preview.getAttribute('srcdoc')).toBeTruthy();
166-
167-
const stopButton = screen.getByRole('button', {
168-
name: /stop sketch/i
169-
});
170-
act(() => {
171-
fireEvent.click(stopButton);
172-
});
173-
expect(preview.getAttribute('srcdoc')).toMatch(/(^|")\s*($|")/);
174-
});
155+
// this test doesn't make sense anymore :/
156+
// how to fix it? could check if sketch gets sent to iframe
157+
// via postmessage or something
158+
// it('clicking on play updates the preview iframe with a srcdoc, stop clears it', () => {
159+
// const playButton = screen.getByRole('button', {
160+
// name: /play only visual sketch/i
161+
// });
162+
// const preview = screen.getByRole('main', { name: /sketch preview/i });
163+
// expect(preview.getAttribute('srcdoc')).toBeFalsy();
164+
// act(() => {
165+
// fireEvent.click(playButton);
166+
// });
167+
168+
// expect(preview.getAttribute('srcdoc')).toBeTruthy();
169+
170+
// const stopButton = screen.getByRole('button', {
171+
// name: /stop sketch/i
172+
// });
173+
// act(() => {
174+
// fireEvent.click(stopButton);
175+
// });
176+
// expect(preview.getAttribute('srcdoc')).toMatch(/(^|")\s*($|")/);
177+
// });
175178
});

client/modules/IDE/actions/ide.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as ActionTypes from '../../../constants';
22
import { clearConsole } from './console';
3+
import { dispatchMessage, MessageTypes } from '../../../utils/dispatcher';
34

45
export function startVisualSketch() {
56
return {
@@ -248,9 +249,23 @@ export function showRuntimeErrorWarning() {
248249
}
249250

250251
export function startSketch() {
251-
return (dispatch) => {
252+
return (dispatch, getState) => {
252253
dispatch(clearConsole());
253-
dispatch(startSketchAndRefresh());
254+
dispatch(startVisualSketch());
255+
dispatch(showRuntimeErrorWarning());
256+
const state = getState();
257+
dispatchMessage({
258+
type: MessageTypes.SKETCH,
259+
payload: {
260+
files: state.files,
261+
basePath: window.location.pathname,
262+
gridOutput: state.preferences.gridOutput,
263+
textOutput: state.preferences.textOutput
264+
}
265+
});
266+
dispatchMessage({
267+
type: MessageTypes.START
268+
});
254269
};
255270
}
256271

@@ -264,6 +279,9 @@ export function startAccessibleSketch() {
264279

265280
export function stopSketch() {
266281
return (dispatch) => {
282+
dispatchMessage({
283+
type: MessageTypes.STOP
284+
});
267285
dispatch(stopAccessibleOutput());
268286
dispatch(stopVisualSketch());
269287
};

client/modules/IDE/actions/preferences.js

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -159,24 +159,6 @@ export function setGridOutput(value) {
159159
};
160160
}
161161

162-
export function setSoundOutput(value) {
163-
return (dispatch, getState) => {
164-
dispatch({
165-
type: ActionTypes.SET_SOUND_OUTPUT,
166-
value
167-
});
168-
const state = getState();
169-
if (state.user.authenticated) {
170-
const formParams = {
171-
preferences: {
172-
soundOutput: value
173-
}
174-
};
175-
updatePreferences(formParams, dispatch);
176-
}
177-
};
178-
}
179-
180162
export function setTheme(value) {
181163
// return {
182164
// type: ActionTypes.SET_THEME,
@@ -225,7 +207,6 @@ export function setAllAccessibleOutput(value) {
225207
return (dispatch) => {
226208
dispatch(setTextOutput(value));
227209
dispatch(setGridOutput(value));
228-
dispatch(setSoundOutput(value));
229210
};
230211
}
231212

client/modules/IDE/actions/project.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function setNewProject(project) {
5454
export function getProject(id, username) {
5555
return (dispatch, getState) => {
5656
dispatch(justOpenedProject());
57-
apiClient
57+
return apiClient
5858
.get(`/${username}/projects/${id}`)
5959
.then((response) => {
6060
dispatch(setProject(response.data));

client/modules/IDE/components/ConsoleInput.jsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import CodeMirror from 'codemirror';
44
import { Encode } from 'console-feed';
55

66
import RightArrowIcon from '../../../images/right-arrow.svg';
7-
import { dispatch } from '../../../utils/dispatcher';
7+
import { dispatchMessage, MessageTypes } from '../../../utils/dispatcher';
88

99
// heavily inspired by
1010
// https://github.com/codesandbox/codesandbox-client/blob/92a1131f4ded6f7d9c16945dc7c18aa97c8ada27/packages/app/src/app/components/Preview/DevTools/Console/Input/index.tsx
@@ -19,7 +19,8 @@ class ConsoleInput extends React.Component {
1919
}
2020

2121
componentDidMount() {
22-
this._cm = CodeMirror(this.codemirrorContainer, { // eslint-disable-line
22+
this._cm = CodeMirror(this.codemirrorContainer, {
23+
// eslint-disable-line
2324
theme: `p5-${this.props.theme}`,
2425
scrollbarStyle: null,
2526
keymap: 'sublime',
@@ -39,9 +40,12 @@ class ConsoleInput extends React.Component {
3940
{ log: Encode({ method: 'command', data: [value] }) }
4041
];
4142
const consoleEvent = [{ method: 'command', data: [value] }];
42-
dispatch({
43-
source: 'console',
44-
messages
43+
dispatchMessage({
44+
type: MessageTypes.EXECUTE,
45+
payload: {
46+
source: 'console',
47+
messages
48+
}
4549
});
4650
this.props.dispatchConsoleEvent(consoleEvent);
4751
cm.setValue('');

client/modules/IDE/components/Editor.jsx

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import babelParser from 'prettier/parser-babel';
77
import htmlParser from 'prettier/parser-html';
88
import cssParser from 'prettier/parser-postcss';
99
import { withTranslation } from 'react-i18next';
10+
import StackTrace from 'stacktrace-js';
1011
import 'codemirror/mode/css/css';
1112
import 'codemirror/addon/selection/active-line';
1213
import 'codemirror/addon/lint/lint';
@@ -115,7 +116,6 @@ class Editor extends React.Component {
115116
styleSelectedText: true,
116117
lint: {
117118
onUpdateLinting: (annotations) => {
118-
this.props.hideRuntimeErrorWarning();
119119
this.updateLintingMessageAccessibility(annotations);
120120
},
121121
options: {
@@ -159,10 +159,11 @@ class Editor extends React.Component {
159159
'change',
160160
debounce(() => {
161161
this.props.setUnsavedChanges(true);
162+
this.props.hideRuntimeErrorWarning();
162163
this.props.updateFileContent(this.props.file.id, this._cm.getValue());
163164
if (this.props.autorefresh && this.props.isPlaying) {
164165
this.props.clearConsole();
165-
this.props.startRefreshSketch();
166+
this.props.startSketch();
166167
}
167168
}, 1000)
168169
);
@@ -246,41 +247,45 @@ class Editor extends React.Component {
246247
);
247248
}
248249

249-
if (prevProps.consoleEvents !== this.props.consoleEvents) {
250-
this.props.showRuntimeErrorWarning();
251-
}
252-
for (let i = 0; i < this._cm.lineCount(); i += 1) {
253-
this._cm.removeLineClass(i, 'background', 'line-runtime-error');
254-
}
255250
if (this.props.runtimeErrorWarningVisible) {
256-
this.props.consoleEvents.forEach((consoleEvent) => {
257-
if (consoleEvent.method === 'error') {
258-
if (
259-
consoleEvent.data &&
260-
consoleEvent.data[0] &&
261-
consoleEvent.data[0].indexOf &&
262-
consoleEvent.data[0].indexOf(')') > -1
263-
) {
264-
const n = consoleEvent.data[0].replace(')', '').split(' ');
265-
const lineNumber = parseInt(n[n.length - 1], 10) - 1;
266-
const { source } = consoleEvent;
267-
const fileName = this.props.file.name;
268-
const errorFromJavaScriptFile = `${source}.js` === fileName;
269-
const errorFromIndexHTML =
270-
source === fileName && fileName === 'index.html';
271-
if (
272-
!Number.isNaN(lineNumber) &&
273-
(errorFromJavaScriptFile || errorFromIndexHTML)
274-
) {
251+
if (this.props.consoleEvents.length !== prevProps.consoleEvents.length) {
252+
this.props.consoleEvents.forEach((consoleEvent) => {
253+
if (consoleEvent.method === 'error') {
254+
// It doesn't work if you create a new Error, but this works
255+
// LOL
256+
const errorObj = { stack: consoleEvent.data[0].toString() };
257+
StackTrace.fromError(errorObj).then((stackLines) => {
258+
this.props.expandConsole();
259+
const line = stackLines.find(
260+
(l) => l.fileName && l.fileName.startsWith('/')
261+
);
262+
if (!line) return;
263+
const fileNameArray = line.fileName.split('/');
264+
const fileName = fileNameArray.slice(-1)[0];
265+
const filePath = fileNameArray.slice(0, -1).join('/');
266+
const fileWithError = this.props.files.find(
267+
(f) => f.name === fileName && f.filePath === filePath
268+
);
269+
this.props.setSelectedFile(fileWithError.id);
275270
this._cm.addLineClass(
276-
lineNumber,
271+
line.lineNumber - 1,
277272
'background',
278273
'line-runtime-error'
279274
);
280-
}
275+
});
281276
}
277+
});
278+
} else {
279+
for (let i = 0; i < this._cm.lineCount(); i += 1) {
280+
this._cm.removeLineClass(i, 'background', 'line-runtime-error');
282281
}
283-
});
282+
}
283+
}
284+
285+
if (this.props.file.id !== prevProps.file.id) {
286+
for (let i = 0; i < this._cm.lineCount(); i += 1) {
287+
this._cm.removeLineClass(i, 'background', 'line-runtime-error');
288+
}
284289
}
285290
}
286291

@@ -440,7 +445,7 @@ Editor.propTypes = {
440445
method: PropTypes.string.isRequired,
441446
args: PropTypes.arrayOf(PropTypes.string)
442447
})
443-
),
448+
).isRequired,
444449
updateLintMessage: PropTypes.func.isRequired,
445450
clearLintMessage: PropTypes.func.isRequired,
446451
updateFileContent: PropTypes.func.isRequired,
@@ -453,7 +458,7 @@ Editor.propTypes = {
453458
url: PropTypes.string
454459
}).isRequired,
455460
setUnsavedChanges: PropTypes.func.isRequired,
456-
startRefreshSketch: PropTypes.func.isRequired,
461+
startSketch: PropTypes.func.isRequired,
457462
autorefresh: PropTypes.bool.isRequired,
458463
isPlaying: PropTypes.bool.isRequired,
459464
theme: PropTypes.string.isRequired,
@@ -471,15 +476,13 @@ Editor.propTypes = {
471476
expandSidebar: PropTypes.func.isRequired,
472477
isUserOwner: PropTypes.bool.isRequired,
473478
clearConsole: PropTypes.func.isRequired,
474-
showRuntimeErrorWarning: PropTypes.func.isRequired,
479+
// showRuntimeErrorWarning: PropTypes.func.isRequired,
475480
hideRuntimeErrorWarning: PropTypes.func.isRequired,
476481
runtimeErrorWarningVisible: PropTypes.bool.isRequired,
477482
provideController: PropTypes.func.isRequired,
478-
t: PropTypes.func.isRequired
479-
};
480-
481-
Editor.defaultProps = {
482-
consoleEvents: []
483+
t: PropTypes.func.isRequired,
484+
setSelectedFile: PropTypes.func.isRequired,
485+
expandConsole: PropTypes.func.isRequired
483486
};
484487

485488
function mapStateToProps(state) {
@@ -496,7 +499,7 @@ function mapStateToProps(state) {
496499
user: state.user,
497500
project: state.project,
498501
toast: state.toast,
499-
console: state.console,
502+
consoleEvents: state.console,
500503

501504
...state.preferences,
502505
...state.ide,

client/modules/IDE/components/Preferences/index.jsx

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -437,23 +437,6 @@ class Preferences extends React.Component {
437437
>
438438
{this.props.t('Preferences.TableText')}
439439
</label>
440-
<input
441-
type="checkbox"
442-
onChange={(event) => {
443-
this.props.setSoundOutput(event.target.checked);
444-
}}
445-
aria-label={this.props.t('Preferences.SoundOutputARIA')}
446-
name="sound output"
447-
id="sound-output-on"
448-
value="On"
449-
checked={this.props.soundOutput}
450-
/>
451-
<label
452-
htmlFor="sound-output-on"
453-
className="preference__option preference__canvas"
454-
>
455-
{this.props.t('Preferences.Sound')}
456-
</label>
457440
</div>
458441
</div>
459442
</TabPanel>
@@ -474,10 +457,8 @@ Preferences.propTypes = {
474457
setLinewrap: PropTypes.func.isRequired,
475458
textOutput: PropTypes.bool.isRequired,
476459
gridOutput: PropTypes.bool.isRequired,
477-
soundOutput: PropTypes.bool.isRequired,
478460
setTextOutput: PropTypes.func.isRequired,
479461
setGridOutput: PropTypes.func.isRequired,
480-
setSoundOutput: PropTypes.func.isRequired,
481462
lintWarning: PropTypes.bool.isRequired,
482463
setLintWarning: PropTypes.func.isRequired,
483464
theme: PropTypes.string.isRequired,

0 commit comments

Comments
 (0)