Skip to content

Commit dbaa8a5

Browse files
committed
Add dispatcher utility to handle message channel api
1 parent 921f136 commit dbaa8a5

File tree

6 files changed

+141
-61
lines changed

6 files changed

+141
-61
lines changed

client/modules/IDE/components/Console.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,10 @@ const Console = ({ t }) => {
144144
})}
145145
</div>
146146
{ isExpanded && isPlaying &&
147-
<ConsoleInput theme={theme} />
147+
<ConsoleInput
148+
theme={theme}
149+
dispatchConsoleEvent={dispatchConsoleEvent}
150+
/>
148151
}
149152
</div>
150153
</section>

client/modules/IDE/components/ConsoleInput.jsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +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';
78

89
class ConsoleInput extends React.Component {
910
componentDidMount() {
@@ -28,13 +29,21 @@ class ConsoleInput extends React.Component {
2829
if (value.trim(' ') === '') {
2930
return false;
3031
}
31-
// need to get access to iframe here?
32-
// could pass "evaluate console function"
33-
// could make a component that handles all of this messaging
34-
window.postMessage([{
35-
log: Encode({ method: 'command', data: Encode(value) }),
36-
source: 'console'
37-
}], '*');
32+
const messages = [{ log: { method: 'command', data: Encode(value) } }];
33+
const consoleEvent = [{ method: 'command', data: Encode(value) }];
34+
dispatch({
35+
source: 'console',
36+
messages
37+
});
38+
this.props.dispatchConsoleEvent(consoleEvent);
39+
40+
// // need to get access to iframe here?
41+
// // could pass "evaluate console function"
42+
// // could make a component that handles all of this messaging
43+
// window.postMessage([{
44+
// log: Encode({ method: 'command', data: Encode(value) }),
45+
// source: 'console'
46+
// }], '*');
3847

3948
cm.setValue('');
4049
}
@@ -80,7 +89,8 @@ class ConsoleInput extends React.Component {
8089
}
8190

8291
ConsoleInput.propTypes = {
83-
theme: PropTypes.string.isRequired
92+
theme: PropTypes.string.isRequired,
93+
dispatchConsoleEvent: PropTypes.func.isRequired
8494
};
8595

8696

client/modules/IDE/components/MessageHandler.jsx

Lines changed: 21 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,20 @@
1-
import React, { useEffect, useCallback } from 'react';
1+
import React, { useEffect } from 'react';
22
import { useDispatch } from 'react-redux';
3-
import { Decode, Encode, Hook, Unhook } from 'console-feed';
3+
import { Decode } from 'console-feed';
44
import { isEqual } from 'lodash';
55
import { dispatchConsoleEvent } from '../actions/console';
66
import { stopSketch, expandConsole } from '../actions/console';
7-
import handleConsoleExpressions from '../../../utils/evaluateConsole';
7+
import { listen } from '../../../utils/dispatcher';
88

9-
function useMessageEvent(callback) {
10-
useEffect(() => {
11-
window.addEventListener('message', callback);
12-
return () => window.removeEventListener('message', callback);
13-
}, [callback]);
14-
}
15-
16-
function MessageHandler() {
9+
function useHandleMessageEvent() {
1710
const dispatch = useDispatch();
1811

19-
const handleMessageEvent = useCallback((messageEvent) => {
20-
if (messageEvent.origin !== window.origin) return;
21-
if (Array.isArray(messageEvent.data)) {
22-
const decodedMessages = messageEvent.data.map(message => Object.assign(
23-
Decode(message.log),
24-
{ source: message.source }
25-
));
12+
const handleMessageEvent = (data) => {
13+
const { source, messages } = data;
14+
if (source === 'sketch' && Array.isArray(messages)) {
15+
const decodedMessages = messages.map(message => Decode(message.log));
2616
decodedMessages.every((message, index, arr) => {
27-
const { data: args, source } = message;
28-
if (source === 'console') {
29-
let consoleInfo = '';
30-
const consoleBuffer = [];
31-
const LOGWAIT = 100;
32-
Hook(window.console, (log) => {
33-
consoleBuffer.push({
34-
log,
35-
source: 'sketch'
36-
});
37-
});
38-
setInterval(() => {
39-
if (consoleBuffer.length > 0) {
40-
window.postMessage(consoleBuffer, '*');
41-
consoleBuffer.length = 0;
42-
}
43-
}, LOGWAIT);
44-
consoleInfo = handleConsoleExpressions(args);
45-
Unhook(window.console);
46-
if (!consoleInfo) {
47-
return false;
48-
}
49-
window.postMessage([{
50-
log: Encode({ method: 'result', data: Encode(consoleInfo) }),
51-
source: 'sketch'
52-
}], '*');
53-
}
17+
const { data: args } = message;
5418
let hasInfiniteLoop = false;
5519
Object.keys(args).forEach((key) => {
5620
if (typeof args[key] === 'string' && args[key].includes('Exiting potential infinite loop')) {
@@ -66,6 +30,7 @@ function MessageHandler() {
6630
Object.assign(message, { times: 1 });
6731
return false;
6832
}
33+
// this should be done in the reducer probs
6934
const cur = Object.assign(message, { times: 1 });
7035
const nextIndex = index + 1;
7136
while (isEqual(cur.data, arr[nextIndex].data) && cur.method === arr[nextIndex].method) {
@@ -77,12 +42,20 @@ function MessageHandler() {
7742
}
7843
return true;
7944
});
80-
8145
dispatch(dispatchConsoleEvent(decodedMessages));
8246
}
83-
});
47+
};
48+
return handleMessageEvent;
49+
}
8450

85-
useMessageEvent(handleMessageEvent);
51+
function MessageHandler() {
52+
const handleMessageEvent = useHandleMessageEvent();
53+
useEffect(() => {
54+
const unsubscribe = listen(handleMessageEvent);
55+
return function cleanup() {
56+
unsubscribe();
57+
};
58+
});
8659
return null;
8760
}
8861

client/modules/IDE/components/PreviewFrame.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '../../../../server/utils/fileUtils';
2020
import { hijackConsoleErrorsScript, startTag, getAllScriptOffsets }
2121
from '../../../utils/consoleUtils';
22+
import { registerFrame } from '../../../utils/dispatcher';
2223

2324

2425
const shouldRenderSketch = (props, prevProps = undefined) => {
@@ -51,6 +52,7 @@ class PreviewFrame extends React.Component {
5152
isAccessibleOutputPlaying: this.props.isAccessibleOutputPlaying
5253
};
5354
if (shouldRenderSketch(props)) this.renderSketch();
55+
registerFrame(this.iframe.current);
5456
}
5557

5658
componentDidUpdate(prevProps) {

client/utils/dispatcher.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Inspired by
2+
// https://github.com/codesandbox/codesandbox-client/blob/master/packages/codesandbox-api/src/dispatcher/index.ts
3+
4+
let frame = null;
5+
let listener = null;
6+
const { origin } = window;
7+
8+
export function registerFrame(newFrame) {
9+
frame = newFrame;
10+
}
11+
12+
function notifyListener(message) {
13+
if (listener) listener(message);
14+
}
15+
16+
function notifyFrame(message) {
17+
const rawMessage = JSON.parse(JSON.stringify(message));
18+
if (frame && frame.postMessage) {
19+
frame.postMessage(rawMessage, origin);
20+
}
21+
}
22+
23+
export function dispatch(message) {
24+
if (!message) return;
25+
26+
notifyListener(message);
27+
notifyFrame(message);
28+
}
29+
30+
/**
31+
* Call callback to remove listener
32+
*/
33+
export function listen(callback) {
34+
listener = callback;
35+
return () => {
36+
listener = null;
37+
};
38+
}
39+
40+
function eventListener(e) {
41+
const { data } = e;
42+
43+
if (data && e.origin === origin) {
44+
notifyListener(data);
45+
}
46+
}
47+
48+
window.addEventListener('message', eventListener);

client/utils/previewEntry.js

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,63 @@
11
import loopProtect from 'loop-protect';
2-
import { Hook } from 'console-feed';
2+
import { Hook, Decode, Encode } from 'console-feed';
3+
import handleConsoleExpressions from './evaluateConsole';
34

45
window.loopProtect = loopProtect;
56

67
const consoleBuffer = [];
78
const LOGWAIT = 500;
89
Hook(window.console, (log) => {
910
consoleBuffer.push({
10-
log,
11-
source: 'sketch'
11+
log
1212
});
1313
});
1414
setInterval(() => {
1515
if (consoleBuffer.length > 0) {
16-
window.parent.postMessage(consoleBuffer, window.origin);
16+
const message = {
17+
messages: consoleBuffer,
18+
source: 'sketch'
19+
};
20+
// this could import dispatch instead! wowowowow
21+
window.parent.postMessage(message, window.origin);
1722
consoleBuffer.length = 0;
1823
}
1924
}, LOGWAIT);
25+
26+
function handleMessageEvent(e) {
27+
console.log('in preview entry handle message event');
28+
if (window.origin !== e.origin) return;
29+
const { data } = e;
30+
const { source, messages } = data;
31+
if (source === 'console' && Array.isArray(messages)) {
32+
const decodedMessages = messages.map(message => Decode(message.log));
33+
decodedMessages.forEach((message) => {
34+
const { data: args } = message;
35+
// let consoleInfo = '';
36+
// const consoleBuffer = [];
37+
// const LOGWAIT = 100;
38+
// Hook(window.console, (log) => {
39+
// consoleBuffer.push({
40+
// log,
41+
// source: 'sketch'
42+
// });
43+
// });
44+
// setInterval(() => {
45+
// if (consoleBuffer.length > 0) {
46+
// window.postMessage(consoleBuffer, '*');
47+
// consoleBuffer.length = 0;
48+
// }
49+
// }, LOGWAIT);
50+
const consoleInfo = handleConsoleExpressions(args);
51+
// Unhook(window.console);
52+
// if (!consoleInfo) {
53+
// return false;
54+
// }
55+
window.postMessage([{
56+
log: Encode({ method: 'result', data: Encode(consoleInfo) }),
57+
source: 'sketch'
58+
}], window.origin);
59+
});
60+
}
61+
}
62+
63+
window.addEventListener('message', handleMessageEvent);

0 commit comments

Comments
 (0)