Summary: We in React VR were need newer Chrome DevTools version to make our plugin work properly. Chromium version with required fixes were shipped in electron 5.0.0. This diff bumps electron version and explicitly allows node integration (accessing require, process and other node related functions from window object in index.html), because it was disabled by default in 5.0.0. Reviewed By: jknoxville Differential Revision: D15693539 fbshipit-source-id: 4f71a07b2afaa489aec46940c924f4ad30ad0413
432 lines
10 KiB
JavaScript
432 lines
10 KiB
JavaScript
/**
|
|
* Copyright 2018-present Facebook.
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
* @format
|
|
*/
|
|
|
|
import type {FlipperPlugin, FlipperDevicePlugin} from './plugin.js';
|
|
import {showOpenDialog} from './utils/exportData.js';
|
|
import {
|
|
setExportDataToFileActiveSheet,
|
|
setActiveSheet,
|
|
ACTIVE_SHEET_SHARE_DATA,
|
|
} from './reducers/application';
|
|
import type {Store} from './reducers/';
|
|
import electron from 'electron';
|
|
import {ENABLE_SHAREABLE_LINK} from 'flipper';
|
|
export type DefaultKeyboardAction = 'clear' | 'goToBottom' | 'createPaste';
|
|
export type TopLevelMenu = 'Edit' | 'View' | 'Window' | 'Help';
|
|
const {dialog} = electron.remote;
|
|
import os from 'os';
|
|
import path from 'path';
|
|
|
|
type MenuItem = {|
|
|
label?: string,
|
|
accelerator?: string,
|
|
role?: string,
|
|
click?: Function,
|
|
submenu?: Array<MenuItem>,
|
|
type?: string,
|
|
enabled?: boolean,
|
|
|};
|
|
|
|
export type KeyboardAction = {|
|
|
action: string,
|
|
label: string,
|
|
accelerator?: string,
|
|
topLevelMenu: TopLevelMenu,
|
|
|};
|
|
|
|
const defaultKeyboardActions: Array<KeyboardAction> = [
|
|
{
|
|
label: 'Clear',
|
|
accelerator: 'CmdOrCtrl+K',
|
|
topLevelMenu: 'View',
|
|
action: 'clear',
|
|
},
|
|
{
|
|
label: 'Go To Bottom',
|
|
accelerator: 'CmdOrCtrl+B',
|
|
topLevelMenu: 'View',
|
|
action: 'goToBottom',
|
|
},
|
|
{
|
|
label: 'Create Paste',
|
|
topLevelMenu: 'Edit',
|
|
action: 'createPaste',
|
|
},
|
|
];
|
|
|
|
export type KeyboardActions = Array<DefaultKeyboardAction | KeyboardAction>;
|
|
|
|
const menuItems: Map<string, Object> = new Map();
|
|
|
|
let pluginActionHandler;
|
|
function actionHandler(action: string) {
|
|
if (pluginActionHandler) {
|
|
pluginActionHandler(action);
|
|
} else {
|
|
console.warn(`Unhandled keyboard action "${action}".`);
|
|
}
|
|
}
|
|
|
|
export function setupMenuBar(
|
|
plugins: Array<Class<FlipperPlugin<> | FlipperDevicePlugin<>>>,
|
|
store: Store,
|
|
) {
|
|
const template = getTemplate(
|
|
electron.remote.app,
|
|
electron.remote.shell,
|
|
store,
|
|
);
|
|
// collect all keyboard actions from all plugins
|
|
const registeredActions: Set<?KeyboardAction> = new Set(
|
|
plugins
|
|
.map(
|
|
(plugin: Class<FlipperPlugin<> | FlipperDevicePlugin<>>) =>
|
|
plugin.keyboardActions || [],
|
|
)
|
|
.reduce((acc: KeyboardActions, cv) => acc.concat(cv), [])
|
|
.map((action: DefaultKeyboardAction | KeyboardAction) =>
|
|
typeof action === 'string'
|
|
? defaultKeyboardActions.find(a => a.action === action)
|
|
: action,
|
|
),
|
|
);
|
|
|
|
// add keyboard actions to
|
|
registeredActions.forEach(keyboardAction => {
|
|
if (keyboardAction != null) {
|
|
appendMenuItem(template, actionHandler, keyboardAction);
|
|
}
|
|
});
|
|
|
|
// create actual menu instance
|
|
const applicationMenu = electron.remote.Menu.buildFromTemplate(template);
|
|
|
|
// add menu items to map, so we can modify them easily later
|
|
registeredActions.forEach(keyboardAction => {
|
|
if (keyboardAction != null) {
|
|
const {topLevelMenu, label, action} = keyboardAction;
|
|
const menu = applicationMenu.items.find(
|
|
menuItem => menuItem.label === topLevelMenu,
|
|
);
|
|
if (menu) {
|
|
// $FlowFixMe submenu is missing in electron API spec
|
|
const menuItem = menu.submenu.items.find(
|
|
menuItem => menuItem.label === label,
|
|
);
|
|
menuItems.set(action, menuItem);
|
|
}
|
|
}
|
|
});
|
|
|
|
// update menubar
|
|
electron.remote.Menu.setApplicationMenu(applicationMenu);
|
|
}
|
|
|
|
function appendMenuItem(
|
|
template: Array<MenuItemConstructorOptions>,
|
|
actionHandler: (action: string) => void,
|
|
item: ?KeyboardAction,
|
|
) {
|
|
const keyboardAction = item;
|
|
if (keyboardAction == null) {
|
|
return;
|
|
}
|
|
const itemIndex = template.findIndex(
|
|
menu => menu.label === keyboardAction.topLevelMenu,
|
|
);
|
|
if (itemIndex > -1 && template[itemIndex].submenu != null) {
|
|
template[itemIndex].submenu.push({
|
|
click: () => actionHandler(keyboardAction.action),
|
|
label: keyboardAction.label,
|
|
accelerator: keyboardAction.accelerator,
|
|
enabled: false,
|
|
});
|
|
}
|
|
}
|
|
|
|
export function activateMenuItems(
|
|
activePlugin: FlipperPlugin<> | FlipperDevicePlugin<>,
|
|
) {
|
|
// disable all keyboard actions
|
|
for (const item of menuItems) {
|
|
item[1].enabled = false;
|
|
}
|
|
|
|
// set plugin action handler
|
|
if (activePlugin.onKeyboardAction) {
|
|
pluginActionHandler = activePlugin.onKeyboardAction;
|
|
}
|
|
|
|
// enable keyboard actions for the current plugin
|
|
if (activePlugin.constructor.keyboardActions != null) {
|
|
(activePlugin.constructor.keyboardActions || []).forEach(keyboardAction => {
|
|
const action =
|
|
typeof keyboardAction === 'string'
|
|
? keyboardAction
|
|
: keyboardAction.action;
|
|
const item = menuItems.get(action);
|
|
if (item != null) {
|
|
item.enabled = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
// set the application menu again to make sure it updates
|
|
electron.remote.Menu.setApplicationMenu(
|
|
electron.remote.Menu.getApplicationMenu(),
|
|
);
|
|
}
|
|
|
|
function getTemplate(
|
|
app: Object,
|
|
shell: Object,
|
|
store: Store,
|
|
): Array<MenuItemConstructorOptions> {
|
|
const exportSubmenu = [
|
|
{
|
|
label: 'File...',
|
|
accelerator: 'CommandOrControl+E',
|
|
click: function(item: Object, focusedWindow: Object) {
|
|
dialog.showSaveDialog(
|
|
null,
|
|
{
|
|
title: 'FlipperExport',
|
|
defaultPath: path.join(os.homedir(), 'FlipperExport.flipper'),
|
|
},
|
|
async file => {
|
|
if (!file) {
|
|
return;
|
|
}
|
|
store.dispatch(setExportDataToFileActiveSheet(file));
|
|
},
|
|
);
|
|
},
|
|
},
|
|
];
|
|
if (ENABLE_SHAREABLE_LINK) {
|
|
exportSubmenu.push({
|
|
label: 'Sharable Link',
|
|
accelerator: 'CommandOrControl+Shift+E',
|
|
click: async function(item: Object, focusedWindow: Object) {
|
|
store.dispatch(setActiveSheet(ACTIVE_SHEET_SHARE_DATA));
|
|
},
|
|
});
|
|
}
|
|
|
|
const template = [
|
|
{
|
|
label: 'File',
|
|
submenu: [
|
|
{
|
|
label: 'Open File...',
|
|
accelerator: 'CommandOrControl+O',
|
|
click: function(item: Object, focusedWindow: Object) {
|
|
showOpenDialog(store);
|
|
},
|
|
},
|
|
{
|
|
label: 'Export',
|
|
submenu: exportSubmenu,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: 'Edit',
|
|
submenu: [
|
|
{
|
|
label: 'Undo',
|
|
accelerator: 'CmdOrCtrl+Z',
|
|
role: 'undo',
|
|
},
|
|
{
|
|
label: 'Redo',
|
|
accelerator: 'Shift+CmdOrCtrl+Z',
|
|
role: 'redo',
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: 'Cut',
|
|
accelerator: 'CmdOrCtrl+X',
|
|
role: 'cut',
|
|
},
|
|
{
|
|
label: 'Copy',
|
|
accelerator: 'CmdOrCtrl+C',
|
|
role: 'copy',
|
|
},
|
|
{
|
|
label: 'Paste',
|
|
accelerator: 'CmdOrCtrl+V',
|
|
role: 'paste',
|
|
},
|
|
{
|
|
label: 'Select All',
|
|
accelerator: 'CmdOrCtrl+A',
|
|
role: 'selectall',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: 'View',
|
|
submenu: [
|
|
{
|
|
label: 'Reload',
|
|
accelerator: 'CmdOrCtrl+R',
|
|
click: function(item: Object, focusedWindow: Object) {
|
|
if (focusedWindow) {
|
|
focusedWindow.reload();
|
|
}
|
|
},
|
|
},
|
|
{
|
|
label: 'Toggle Full Screen',
|
|
accelerator: (function() {
|
|
if (process.platform === 'darwin') {
|
|
return 'Ctrl+Command+F';
|
|
} else {
|
|
return 'F11';
|
|
}
|
|
})(),
|
|
click: function(item: Object, focusedWindow: Object) {
|
|
if (focusedWindow) {
|
|
focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
|
|
}
|
|
},
|
|
},
|
|
{
|
|
label: 'Toggle Developer Tools',
|
|
accelerator: (function() {
|
|
if (process.platform === 'darwin') {
|
|
return 'Alt+Command+I';
|
|
} else {
|
|
return 'Ctrl+Shift+I';
|
|
}
|
|
})(),
|
|
click: function(item: Object, focusedWindow: Object) {
|
|
if (focusedWindow) {
|
|
focusedWindow.toggleDevTools();
|
|
}
|
|
},
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: 'Window',
|
|
role: 'window',
|
|
submenu: [
|
|
{
|
|
label: 'Minimize',
|
|
accelerator: 'CmdOrCtrl+M',
|
|
role: 'minimize',
|
|
},
|
|
{
|
|
label: 'Close',
|
|
accelerator: 'CmdOrCtrl+W',
|
|
role: 'close',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: 'Help',
|
|
role: 'help',
|
|
submenu: [
|
|
{
|
|
label: 'Getting started',
|
|
click: function() {
|
|
shell.openExternal(
|
|
'https://fbflipper.com/docs/getting-started.html',
|
|
);
|
|
},
|
|
},
|
|
{
|
|
label: 'Create plugins',
|
|
click: function() {
|
|
shell.openExternal(
|
|
'https://fbflipper.com/docs/tutorial/intro.html',
|
|
);
|
|
},
|
|
},
|
|
{
|
|
label: 'Report problems',
|
|
click: function() {
|
|
shell.openExternal('https://github.com/facebook/flipper/issues');
|
|
},
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
if (process.platform === 'darwin') {
|
|
const name = app.getName();
|
|
template.unshift({
|
|
label: name,
|
|
submenu: [
|
|
{
|
|
label: 'About ' + name,
|
|
role: 'about',
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: 'Services',
|
|
role: 'services',
|
|
submenu: [],
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: 'Hide ' + name,
|
|
accelerator: 'Command+H',
|
|
role: 'hide',
|
|
},
|
|
{
|
|
label: 'Hide Others',
|
|
accelerator: 'Command+Shift+H',
|
|
role: 'hideothers',
|
|
},
|
|
{
|
|
label: 'Show All',
|
|
role: 'unhide',
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: 'Quit',
|
|
accelerator: 'Command+Q',
|
|
click: function() {
|
|
app.quit();
|
|
},
|
|
},
|
|
],
|
|
});
|
|
const windowMenu = template.find(function(m: Object) {
|
|
return m.role === 'window';
|
|
});
|
|
if (windowMenu) {
|
|
windowMenu.submenu.push(
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: 'Bring All to Front',
|
|
role: 'front',
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
return template;
|
|
}
|