Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[macOS] Add TouchBar integration #55

Merged
merged 12 commits into from
Mar 13, 2017
5 changes: 4 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
"strict": 0,
"no-console": 0,
"no-param-reassign": ["error", { "props": false }],
"no-underscore-dangle": ["error", { "allow": ["__IS_REDUX_NATIVE_MESSAGE__"] }],
"no-underscore-dangle": [
"error",
{ "allow": ["__IS_REDUX_NATIVE_MESSAGE__", "__AVAILABLE_METHODS_CAN_CALL_BY_RNDEBUGGER__"] }
],
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"import/prefer-default-export": 0
}
Expand Down
7 changes: 6 additions & 1 deletion app/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { webFrame } from 'electron';
import { remote, webFrame } from 'electron';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App';
import configureStore from './store/configureStore';

if (process.platform === 'darwin') {
// Reset TouchBar when reload the app
remote.getCurrentWindow().setTouchBar([]);
}

webFrame.setZoomFactor(1);
webFrame.setZoomLevelLimits(1, 1);

Expand Down
10 changes: 10 additions & 0 deletions app/middlewares/debuggerAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import WebSocket from 'ws';
import { bindActionCreators } from 'redux';
import Worker from 'worker?name=RNDebuggerWorker.js!../worker'; // eslint-disable-line
import * as debuggerActions from '../actions/debugger';
import { setAvailableDevMenuMethods } from './touchBarBuilder';

const { SET_DEBUGGER_LOCATION } = debuggerActions;

Expand All @@ -26,6 +27,11 @@ const workerOnMessage = message => {
if (message.data && message.data.__IS_REDUX_NATIVE_MESSAGE__) {
return true;
}
const list = message.data && message.data.__AVAILABLE_METHODS_CAN_CALL_BY_RNDEBUGGER__;
if (list) {
setAvailableDevMenuMethods(list, worker);
return false;
}
socket.send(JSON.stringify(message.data));
};

Expand All @@ -47,6 +53,7 @@ const shutdownJSRuntime = (status, statusMessage) => {
const { setDebuggerWorker } = actions;
if (worker) {
worker.terminate();
setAvailableDevMenuMethods([]);
}
worker = null;
setDebuggerWorker(null, status, statusMessage);
Expand Down Expand Up @@ -85,6 +92,9 @@ const connectToDebuggerProxy = () => {
} else {
// Otherwise, pass through to the worker.
if (!worker) return;
if (object.method === 'executeApplicationScript') {
object.enableNetworkInspect = localStorage.enableNetworkInspect === 'enabled';
}
worker.postMessage(object);
}
};
Expand Down
23 changes: 21 additions & 2 deletions app/middlewares/reduxAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import { bindActionCreators } from 'redux';
import { UPDATE_STATE, LIFTED_ACTION } from 'remotedev-app/lib/constants/actionTypes';
import { DISCONNECTED } from 'remotedev-app/lib/constants/socketActionTypes';
import { nonReduxDispatch } from 'remotedev-app/lib/utils/monitorActions';
import { showNotification } from 'remotedev-app/lib/actions';
import { showNotification, liftedDispatch } from 'remotedev-app/lib/actions';
import { getActiveInstance } from 'remotedev-app/lib/reducers/instances';
import { SET_DEBUGGER_WORKER } from '../actions/debugger';
import { setReduxDevToolsMethods, updateSliderContent } from './touchBarBuilder';

const unboundActions = {
showNotification,
updateState: request => ({
type: UPDATE_STATE,
request: request.data ? { ...request.data, id: request.id } : request,
}),
liftedDispatch,
};
let actions;
let worker;
Expand Down Expand Up @@ -67,10 +69,27 @@ export default inStore => {
initWorker(action.worker);
} else {
removeWorker(action.worker);
setReduxDevToolsMethods(false);
next({ type: DISCONNECTED });
}
}
if (action.type === LIFTED_ACTION) toWorker(action);
if (action.type === LIFTED_ACTION) {
toWorker(action);
}
if (action.type === UPDATE_STATE || action.type === LIFTED_ACTION) {
next(action);
const state = store.getState();
const instances = state.instances;
const id = getActiveInstance(instances);
const liftedState = instances.states[id];
if (liftedState && liftedState.computedStates.length > 1) {
setReduxDevToolsMethods(true, actions.liftedDispatch);
} else if (liftedState && liftedState.computedStates.length <= 1) {
setReduxDevToolsMethods(false);
}
updateSliderContent(liftedState, action.action && action.action.dontUpdateTouchBarSlider);
return;
}
return next(action);
};
};
123 changes: 123 additions & 0 deletions app/middlewares/touchBarBuilder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { remote } from 'electron';

const { TouchBarButton, TouchBarSlider } = remote.TouchBar || {};
const currentWindow = remote.getCurrentWindow();

let worker;
const leftBar = {
reload: null,
enableNetworkInspect: null,
};

let sliderEnabled;
let storeLiftedState;
const rightBar = {
slider: null,
prev: null,
next: null,
};

const resetTouchBar = () => {
const touchBar = [
...Object.keys(leftBar).filter(key => !!leftBar[key]).map(key => leftBar[key]),
...(sliderEnabled ?
Object.keys(rightBar).filter(key => !!rightBar[key]).map(key => rightBar[key]) :
[]
),
];
currentWindow.setTouchBar(touchBar);
};

const invokeDevMenuMethod = ({ name, args }) =>
worker.postMessage({ method: 'invokeDevMenuMethod', name, args });

export const setAvailableDevMenuMethods = (list, wkr) => {
if (process.platform !== 'darwin') return;

worker = wkr;

leftBar.reload = null;
leftBar.enableNetworkInspect = null;
if (list.includes('reload')) {
leftBar.reload = new TouchBarButton({
label: 'Reload JS',
click: () => {
invokeDevMenuMethod({ name: 'reload' });
},
});
}

if (list.includes('enableNetworkInspect')) {
const disabled = () => localStorage.enableNetworkInspect === 'disabled';
const getLabel = () => (disabled() ? 'Disable Network Inspect' : 'Enable Network Inspect');
const toggle = () => (disabled() ? 'enabled' : 'disabled');
leftBar.enableNetworkInspect = new TouchBarButton({
label: getLabel(),
click: () => {
localStorage.enableNetworkInspect = toggle();
leftBar.enableNetworkInspect.label = getLabel();
invokeDevMenuMethod({
name: 'enableNetworkInspect',
args: [localStorage.enableNetworkInspect],
});
},
});
}

resetTouchBar();
};

export const setReduxDevToolsMethods = (enabled, dispatch) => {
if (process.platform !== 'darwin') return;

// Already setup
if (enabled && sliderEnabled) return;

const handleSliderChange = (nextIndex, dontUpdateTouchBarSlider = false) =>
dispatch({
type: 'JUMP_TO_STATE',
actionId: storeLiftedState.stagedActionIds[nextIndex],
index: nextIndex,
dontUpdateTouchBarSlider,
});

rightBar.slider = new TouchBarSlider({
value: 0,
minValue: 0,
maxValue: 0,
change(nextIndex) {
if (nextIndex !== storeLiftedState.currentStateIndex) {
handleSliderChange(nextIndex, true);
}
},
});
rightBar.prev = new TouchBarButton({
label: 'Prev',
click() {
const nextIndex = storeLiftedState.currentStateIndex - 1;
if (nextIndex >= 0) {
handleSliderChange(nextIndex);
}
},
});
rightBar.next = new TouchBarButton({
label: 'Next',
click() {
const nextIndex = storeLiftedState.currentStateIndex + 1;
if (nextIndex < storeLiftedState.computedStates.length) {
handleSliderChange(nextIndex);
}
},
});
sliderEnabled = enabled;
resetTouchBar();
};

export const updateSliderContent = (liftedState, dontUpdateTouchBarSlider) => {
storeLiftedState = liftedState;
if (sliderEnabled && !dontUpdateTouchBarSlider) {
const { currentStateIndex, computedStates } = liftedState;
rightBar.slider.maxValue = computedStates.length - 1;
rightBar.slider.value = currentStateIndex;
}
};
57 changes: 57 additions & 0 deletions app/worker/devMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable no-underscore-dangle */

// Avoid warning of use `window.require` on dev mode
const avoidWarnForRequire = (moduleName = 'NativeModules') => new Promise(resolve =>
setTimeout(() => {
// It's replaced console.warn of react-native
const originalWarn = console.warn;
console.warn = (...args) => {
if (args[0] && args[0].indexOf(`Requiring module '${moduleName}' by name`) > -1) return;
return originalWarn(...args);
};
resolve(() => { console.warn = originalWarn; });
})
);

const toggleNetworkInspect = enabled => {
if (!enabled && window.__Network_INSPECT__) {
window.XMLHttpRequest = window.__Network_INSPECT__.XMLHttpRequest;
window.FormData = window.__Network_INSPECT__.FormData;
delete window.__Network_INSPECT__;
return;
}
if (!enabled) return;
window.__Network_INSPECT__ = {
XMLHttpRequest: window.XMLHttpRequest,
FormData: window.FormData,
};
window.XMLHttpRequest = window.originalXMLHttpRequest ?
window.originalXMLHttpRequest :
window.XMLHttpRequest;
window.FormData = window.originalFormData ?
window.originalFormData :
window.FormData;
};

export const checkAvailableDevMenuMethods = async (enableNetworkInspect = false) => {
const done = await avoidWarnForRequire();
const { DevMenu } = window.require('NativeModules');
done();

let result = ['enableNetworkInspect'];
window.__AVAILABLE_METHODS_CAN_CALL_BY_RNDEBUGGER__ = {
enableNetworkInspect: toggleNetworkInspect,
};
if (DevMenu && DevMenu.reload) {
window.__AVAILABLE_METHODS_CAN_CALL_BY_RNDEBUGGER__.reload = DevMenu.reload;
result = ['reload', ...result];
}

toggleNetworkInspect(enableNetworkInspect);
postMessage({ __AVAILABLE_METHODS_CAN_CALL_BY_RNDEBUGGER__: result });
};

export const invokeDevMenuMethod = (name, args = []) => {
const method = window.__AVAILABLE_METHODS_CAN_CALL_BY_RNDEBUGGER__[name];
if (method) method(...args);
};
15 changes: 12 additions & 3 deletions app/worker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@

// Edit from https://github.com/facebook/react-native/blob/master/local-cli/server/util/debuggerWorker.js

// NOTE: WebWorker not have `global`
/* eslint-disable no-underscore-dangle */
import { checkAvailableDevMenuMethods, invokeDevMenuMethod } from './devMenu';

// WebWorker not have `global`
self.global = self;

// redux store enhancer
const devTools = require('./reduxAPI');

/* eslint-disable no-underscore-dangle */

self.__REMOTEDEV__ = require('./remotedev');

devTools.default.send = self.__REMOTEDEV__.send;
Expand Down Expand Up @@ -46,6 +48,8 @@ const messageHandlers = {
if (!self.__RND_INTERVAL__) {
self.__RND_INTERVAL__ = setInterval(function(){}, 100); // eslint-disable-line
}

checkAvailableDevMenuMethods(message.enableNetworkInspect);
},
};

Expand All @@ -57,6 +61,11 @@ addEventListener('message', message => {
return true;
}

if (object.method === 'invokeDevMenuMethod') {
invokeDevMenuMethod(object.name, object.args);
return false;
}

const sendReply = (result, error) => {
postMessage({ replyID: object.id, result, error });
};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"babel-preset-stage-0": "^6.5.0",
"cross-env": "^3.1.3",
"devtron": "^1.2.0",
"electron": "^1.6.0",
"electron": "^1.6.3",
"electron-debug": "^1.0.0",
"electron-devtools-installer": "^2.1.0",
"electron-packager": "^8.2.0",
Expand Down
14 changes: 4 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1674,9 +1674,9 @@ electron-packager@^8.2.0:
sanitize-filename "^1.6.0"
semver "^5.3.0"

electron@^1.6.0:
version "1.6.1"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.6.1.tgz#aa426e83bfb5919c3a492297eff368b67e66f1ae"
electron@^1.6.3:
version "1.6.3"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.6.3.tgz#95fc62899e50838563fa768baa2011ea860d04b1"
dependencies:
electron-download "^3.0.1"
extract-zip "^1.0.3"
Expand Down Expand Up @@ -5040,7 +5040,7 @@ sumchecker@^2.0.1:
dependencies:
debug "^2.2.0"

supports-color@3.1.2:
supports-color@3.1.2, supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
dependencies:
Expand All @@ -5054,12 +5054,6 @@ supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"

supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
dependencies:
has-flag "^1.0.0"

symbol-observable@^0.2.4:
version "0.2.4"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40"
Expand Down