From 9e9146690c8c10cbbcd33a6d88f082f3f48ede8b Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Thu, 3 Jan 2019 16:14:25 +0200 Subject: [PATCH 01/19] Move zalmoxisus/remotedev-app --- packages/redux-devtools-app/.babelrc | 4 + packages/redux-devtools-app/.eslintrc | 51 +++ packages/redux-devtools-app/LICENSE | 21 ++ packages/redux-devtools-app/README.md | 44 +++ .../assets/electron/index.js | 201 ++++++++++++ .../assets/electron/package.json | 9 + .../assets/electron/resources/config.json | 17 + .../assets/electron/resources/osx/icon.icns | Bin 0 -> 50428 bytes .../electron/resources/osx/installer.png | Bin 0 -> 19725 bytes .../electron/resources/windows/icon.ico | Bin 0 -> 99678 bytes packages/redux-devtools-app/assets/index.html | 45 +++ packages/redux-devtools-app/package.json | 112 +++++++ .../src/app/actions/index.js | 110 +++++++ .../src/app/components/BottomButtons.js | 56 ++++ .../src/app/components/Header.js | 78 +++++ .../src/app/components/InstanceSelector.js | 47 +++ .../src/app/components/MonitorSelector.js | 45 +++ .../src/app/components/Settings/Connection.js | 126 ++++++++ .../src/app/components/Settings/Themes.js | 64 ++++ .../src/app/components/Settings/index.js | 33 ++ .../src/app/components/TopButtons.js | 102 ++++++ .../components/buttons/DispatcherButton.js | 39 +++ .../app/components/buttons/ExportButton.js | 37 +++ .../app/components/buttons/ImportButton.js | 65 ++++ .../src/app/components/buttons/LockButton.js | 40 +++ .../app/components/buttons/PersistButton.js | 48 +++ .../src/app/components/buttons/PrintButton.js | 40 +++ .../app/components/buttons/RecordButton.js | 38 +++ .../app/components/buttons/SliderButton.js | 39 +++ .../src/app/components/buttons/SyncButton.js | 45 +++ .../src/app/constants/actionTypes.js | 24 ++ .../src/app/constants/dataTypes.js | 2 + .../src/app/constants/socketActionTypes.js | 19 ++ .../src/app/constants/socketOptions.js | 12 + .../src/app/containers/Actions.js | 80 +++++ .../src/app/containers/App.js | 58 ++++ .../src/app/containers/DevTools.js | 88 ++++++ .../monitors/ChartMonitorWrapper.js | 55 ++++ .../src/app/containers/monitors/Dispatcher.js | 201 ++++++++++++ .../monitors/InspectorWrapper/ChartTab.js | 109 +++++++ .../monitors/InspectorWrapper/RawTab.js | 29 ++ .../monitors/InspectorWrapper/SubTabs.js | 111 +++++++ .../InspectorWrapper/VisualDiffTab.js | 226 +++++++++++++ .../monitors/InspectorWrapper/index.js | 49 +++ .../src/app/containers/monitors/Slider.js | 39 +++ packages/redux-devtools-app/src/app/index.js | 40 +++ .../src/app/middlewares/api.js | 201 ++++++++++++ .../src/app/middlewares/exportState.js | 48 +++ .../src/app/reducers/connection.js | 15 + .../src/app/reducers/index.js | 22 ++ .../src/app/reducers/instances.js | 299 ++++++++++++++++++ .../src/app/reducers/monitor.js | 69 ++++ .../src/app/reducers/notification.js | 16 + .../src/app/reducers/reports.js | 37 +++ .../src/app/reducers/section.js | 8 + .../src/app/reducers/socket.js | 74 +++++ .../src/app/reducers/theme.js | 15 + .../src/app/store/configureStore.js | 39 +++ .../src/app/utils/commitExcessActions.js | 29 ++ .../src/app/utils/getMonitor.js | 22 ++ .../src/app/utils/monitorActions.js | 50 +++ .../src/app/utils/parseJSON.js | 34 ++ .../src/app/utils/stringifyJSON.js | 17 + .../src/app/utils/updateState.js | 32 ++ packages/redux-devtools-app/src/index.js | 25 ++ .../test/__mocks__/styleMock.js | 1 + .../redux-devtools-app/test/index.spec.js | 41 +++ packages/redux-devtools-app/webpack.config.js | 82 +++++ .../redux-devtools-app/webpack.config.umd.js | 67 ++++ yarn.lock | 253 +++++++++++++-- 70 files changed, 4169 insertions(+), 25 deletions(-) create mode 100644 packages/redux-devtools-app/.babelrc create mode 100644 packages/redux-devtools-app/.eslintrc create mode 100644 packages/redux-devtools-app/LICENSE create mode 100644 packages/redux-devtools-app/README.md create mode 100644 packages/redux-devtools-app/assets/electron/index.js create mode 100644 packages/redux-devtools-app/assets/electron/package.json create mode 100644 packages/redux-devtools-app/assets/electron/resources/config.json create mode 100644 packages/redux-devtools-app/assets/electron/resources/osx/icon.icns create mode 100644 packages/redux-devtools-app/assets/electron/resources/osx/installer.png create mode 100644 packages/redux-devtools-app/assets/electron/resources/windows/icon.ico create mode 100644 packages/redux-devtools-app/assets/index.html create mode 100644 packages/redux-devtools-app/package.json create mode 100644 packages/redux-devtools-app/src/app/actions/index.js create mode 100644 packages/redux-devtools-app/src/app/components/BottomButtons.js create mode 100644 packages/redux-devtools-app/src/app/components/Header.js create mode 100644 packages/redux-devtools-app/src/app/components/InstanceSelector.js create mode 100644 packages/redux-devtools-app/src/app/components/MonitorSelector.js create mode 100644 packages/redux-devtools-app/src/app/components/Settings/Connection.js create mode 100644 packages/redux-devtools-app/src/app/components/Settings/Themes.js create mode 100644 packages/redux-devtools-app/src/app/components/Settings/index.js create mode 100644 packages/redux-devtools-app/src/app/components/TopButtons.js create mode 100644 packages/redux-devtools-app/src/app/components/buttons/DispatcherButton.js create mode 100644 packages/redux-devtools-app/src/app/components/buttons/ExportButton.js create mode 100644 packages/redux-devtools-app/src/app/components/buttons/ImportButton.js create mode 100644 packages/redux-devtools-app/src/app/components/buttons/LockButton.js create mode 100644 packages/redux-devtools-app/src/app/components/buttons/PersistButton.js create mode 100644 packages/redux-devtools-app/src/app/components/buttons/PrintButton.js create mode 100644 packages/redux-devtools-app/src/app/components/buttons/RecordButton.js create mode 100644 packages/redux-devtools-app/src/app/components/buttons/SliderButton.js create mode 100644 packages/redux-devtools-app/src/app/components/buttons/SyncButton.js create mode 100644 packages/redux-devtools-app/src/app/constants/actionTypes.js create mode 100644 packages/redux-devtools-app/src/app/constants/dataTypes.js create mode 100644 packages/redux-devtools-app/src/app/constants/socketActionTypes.js create mode 100644 packages/redux-devtools-app/src/app/constants/socketOptions.js create mode 100644 packages/redux-devtools-app/src/app/containers/Actions.js create mode 100644 packages/redux-devtools-app/src/app/containers/App.js create mode 100644 packages/redux-devtools-app/src/app/containers/DevTools.js create mode 100644 packages/redux-devtools-app/src/app/containers/monitors/ChartMonitorWrapper.js create mode 100644 packages/redux-devtools-app/src/app/containers/monitors/Dispatcher.js create mode 100644 packages/redux-devtools-app/src/app/containers/monitors/InspectorWrapper/ChartTab.js create mode 100644 packages/redux-devtools-app/src/app/containers/monitors/InspectorWrapper/RawTab.js create mode 100644 packages/redux-devtools-app/src/app/containers/monitors/InspectorWrapper/SubTabs.js create mode 100644 packages/redux-devtools-app/src/app/containers/monitors/InspectorWrapper/VisualDiffTab.js create mode 100644 packages/redux-devtools-app/src/app/containers/monitors/InspectorWrapper/index.js create mode 100644 packages/redux-devtools-app/src/app/containers/monitors/Slider.js create mode 100644 packages/redux-devtools-app/src/app/index.js create mode 100644 packages/redux-devtools-app/src/app/middlewares/api.js create mode 100644 packages/redux-devtools-app/src/app/middlewares/exportState.js create mode 100644 packages/redux-devtools-app/src/app/reducers/connection.js create mode 100644 packages/redux-devtools-app/src/app/reducers/index.js create mode 100644 packages/redux-devtools-app/src/app/reducers/instances.js create mode 100644 packages/redux-devtools-app/src/app/reducers/monitor.js create mode 100644 packages/redux-devtools-app/src/app/reducers/notification.js create mode 100644 packages/redux-devtools-app/src/app/reducers/reports.js create mode 100644 packages/redux-devtools-app/src/app/reducers/section.js create mode 100644 packages/redux-devtools-app/src/app/reducers/socket.js create mode 100644 packages/redux-devtools-app/src/app/reducers/theme.js create mode 100644 packages/redux-devtools-app/src/app/store/configureStore.js create mode 100644 packages/redux-devtools-app/src/app/utils/commitExcessActions.js create mode 100644 packages/redux-devtools-app/src/app/utils/getMonitor.js create mode 100644 packages/redux-devtools-app/src/app/utils/monitorActions.js create mode 100644 packages/redux-devtools-app/src/app/utils/parseJSON.js create mode 100644 packages/redux-devtools-app/src/app/utils/stringifyJSON.js create mode 100644 packages/redux-devtools-app/src/app/utils/updateState.js create mode 100644 packages/redux-devtools-app/src/index.js create mode 100644 packages/redux-devtools-app/test/__mocks__/styleMock.js create mode 100644 packages/redux-devtools-app/test/index.spec.js create mode 100644 packages/redux-devtools-app/webpack.config.js create mode 100644 packages/redux-devtools-app/webpack.config.umd.js diff --git a/packages/redux-devtools-app/.babelrc b/packages/redux-devtools-app/.babelrc new file mode 100644 index 00000000..f0a6f40f --- /dev/null +++ b/packages/redux-devtools-app/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": [ "es2015", "stage-0", "react" ], + "plugins": [ "add-module-exports", "transform-decorators-legacy" ] +} \ No newline at end of file diff --git a/packages/redux-devtools-app/.eslintrc b/packages/redux-devtools-app/.eslintrc new file mode 100644 index 00000000..76f0c9bd --- /dev/null +++ b/packages/redux-devtools-app/.eslintrc @@ -0,0 +1,51 @@ +{ + "extends": "eslint-config-airbnb", + "globals": { + "chrome": true, + "electron": true + }, + "env": { + "jest": true, + "browser": true, + "node": true + }, + "parser": "babel-eslint", + "rules": { + "react/prefer-stateless-function": 0, + "react/no-array-index-key": 0, + "react/forbid-prop-types": 0, + "react/require-default-props": 0, + "react/jsx-filename-extension": 0, + "react/jsx-uses-react": 2, + "react/jsx-uses-vars": 2, + "react/react-in-jsx-scope": 2, + "react/sort-comp": 0, + "react/jsx-quotes": 0, + "import/no-extraneous-dependencies": 0, + "block-scoped-var": 0, + "padded-blocks": 0, + "quotes": [ 1, "single" ], + "comma-style": [ 2, "last" ], + "eol-last": 0, + "no-unused-vars": 0, + "no-console": 0, + "func-names": 0, + "prefer-const": 0, + "comma-dangle": 0, + "id-length": 0, + "no-use-before-define": 0, + "indent": [2, 2, {"SwitchCase": 1}], + "new-cap": [2, { "capIsNewExceptions": ["Test"] }], + "no-underscore-dangle": 0, + "no-plusplus": 0, + "arrow-parens": 0, + "prefer-template": 0, + "class-methods-use-this": 0, + "max-len": ["error", { "code": 120 }], + "no-mixed-operators": 0, + "no-undef": 0 + }, + "plugins": [ + "react" + ] +} \ No newline at end of file diff --git a/packages/redux-devtools-app/LICENSE b/packages/redux-devtools-app/LICENSE new file mode 100644 index 00000000..01698ebb --- /dev/null +++ b/packages/redux-devtools-app/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Mihail Diordiev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/redux-devtools-app/README.md b/packages/redux-devtools-app/README.md new file mode 100644 index 00000000..5a10623e --- /dev/null +++ b/packages/redux-devtools-app/README.md @@ -0,0 +1,44 @@ +Remote Redux DevTools monitor app +================================== + +![Demo](https://raw.githubusercontent.com/zalmoxisus/remote-redux-devtools/master/demo.gif) + +Web, Electron and Chrome app for monitoring [remote-redux-devtools](https://github.com/zalmoxisus/remote-redux-devtools). Can be accessed on [`remotedev.io`](http://remotedev.io/local). + +Also it's a react component you can use to build amazing monitor applications like: + +* [redux-devtools-extension](https://github.com/zalmoxisus/redux-devtools-extension). +* [react-native-debugger](https://github.com/jhen0409/react-native-debugger) - Electron app, which already includes `remotedev-server`, `remotedev-app` and even React DevTools. +* [remote-redux-devtools-on-debugger](https://github.com/jhen0409/remote-redux-devtools-on-debugger) - Used in React Native debugger as a dock monitor. +* [atom-redux-devtools](https://github.com/zalmoxisus/atom-redux-devtools) - Used in Atom editor. +* [vscode-redux-devtools](https://github.com/jkzing/vscode-redux-devtools) - Used in Visual Studio Code. + +### Usage + +```js +import React from 'react'; +import ReactDom from 'react-dom'; +import DevToolsApp from 'remotedev-app'; + +ReactDom.render( + , + document.getElementById('root') +); + +``` + +### Parameters + +* `socketOptions` - *object* used to specify predefined options for the connection: + * `hostname` - *string* + * `port` - *number or string* + * `autoReconnect` - *boolean* + * `secure` - *boolean*. +* `monitorOptions` - *object* used to specify predefined monitor options: + * `selected` - *string* - which monitor is selected by default. One of the following values: `LogMonitor`, `InspectorMonitor`, `ChartMonitor`. +* `testTemplates` - *array* of strings representing predefined test templates. +* `noSettings` - *boolean* set to `true` in order to hide settings button and dialog. + +### License + +MIT diff --git a/packages/redux-devtools-app/assets/electron/index.js b/packages/redux-devtools-app/assets/electron/index.js new file mode 100644 index 00000000..261417a9 --- /dev/null +++ b/packages/redux-devtools-app/assets/electron/index.js @@ -0,0 +1,201 @@ +const { app, BrowserWindow, Menu, shell } = require('electron'); + +let menu; +let template; +let win = null; + +// require('electron-debug')(); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') app.quit(); +}); + +app.on('ready', () => { + win = new BrowserWindow({ width: 1024, height: 728 }); + + win.loadURL(`file://${__dirname}/index.html`); + + win.on('closed', () => { + win = null; + }); + + if (process.platform === 'darwin') { + template = [{ + label: 'Electron', + submenu: [{ + label: 'About', + selector: 'orderFrontStandardAboutPanel:' + }, { + type: 'separator' + }, { + label: 'Services', + submenu: [] + }, { + type: 'separator' + }, { + label: 'Hide', + accelerator: 'Command+H', + selector: 'hide:' + }, { + label: 'Hide Others', + accelerator: 'Command+Shift+H', + selector: 'hideOtherApplications:' + }, { + label: 'Show All', + selector: 'unhideAllApplications:' + }, { + type: 'separator' + }, { + label: 'Quit', + accelerator: 'Command+Q', + click() { + app.quit(); + } + }] + }, { + label: 'Edit', + submenu: [{ + label: 'Undo', + accelerator: 'Command+Z', + selector: 'undo:' + }, { + label: 'Redo', + accelerator: 'Shift+Command+Z', + selector: 'redo:' + }, { + type: 'separator' + }, { + label: 'Cut', + accelerator: 'Command+X', + selector: 'cut:' + }, { + label: 'Copy', + accelerator: 'Command+C', + selector: 'copy:' + }, { + label: 'Paste', + accelerator: 'Command+V', + selector: 'paste:' + }, { + label: 'Select All', + accelerator: 'Command+A', + selector: 'selectAll:' + }] + }, { + label: 'View', + submenu: (process.env.NODE_ENV === 'development') ? [{ + label: 'Reload', + accelerator: 'Command+R', + click() { + win.restart(); + } + }, { + label: 'Toggle Full Screen', + accelerator: 'Ctrl+Command+F', + click() { + win.setFullScreen(!win.isFullScreen()); + } + }, { + label: 'Toggle Developer Tools', + accelerator: 'Alt+Command+I', + click() { + win.toggleDevTools(); + } + }] : [{ + label: 'Toggle Full Screen', + accelerator: 'Ctrl+Command+F', + click() { + win.setFullScreen(!win.isFullScreen()); + } + }] + }, { + label: 'Window', + submenu: [{ + label: 'Minimize', + accelerator: 'Command+M', + selector: 'performMiniaturize:' + }, { + label: 'Close', + accelerator: 'Command+W', + selector: 'performClose:' + }, { + type: 'separator' + }, { + label: 'Bring All to Front', + selector: 'arrangeInFront:' + }] + }, { + label: 'Help', + submenu: [{ + label: 'Learn More', + click() { + shell.openExternal('http://electron.atom.io'); + } + }, { + label: 'Search Issues', + click() { + shell.openExternal('https://github.com/atom/electron/issues'); + } + }] + }]; + + menu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(menu); + } else { + template = [{ + label: '&File', + submenu: [{ + label: '&Open', + accelerator: 'Ctrl+O' + }, { + label: '&Close', + accelerator: 'Ctrl+W', + click() { + win.close(); + } + }] + }, { + label: '&View', + submenu: (process.env.NODE_ENV === 'development') ? [{ + label: '&Reload', + accelerator: 'Ctrl+R', + click() { + win.restart(); + } + }, { + label: 'Toggle &Full Screen', + accelerator: 'F11', + click() { + win.setFullScreen(!win.isFullScreen()); + } + }, { + label: 'Toggle &Developer Tools', + accelerator: 'Alt+Ctrl+I', + click() { + win.toggleDevTools(); + } + }] : [{ + label: 'Toggle &Full Screen', + accelerator: 'F11', + click() { + win.setFullScreen(!win.isFullScreen()); + } + }] + }, { + label: 'Help', + submenu: [{ + label: 'Learn More', + click() { + shell.openExternal('http://electron.atom.io'); + } + }, { + label: 'Search Issues', + click() { + shell.openExternal('https://github.com/atom/electron/issues'); + } + }] + }]; + menu = Menu.buildFromTemplate(template); + win.setMenu(menu); + } +}); diff --git a/packages/redux-devtools-app/assets/electron/package.json b/packages/redux-devtools-app/assets/electron/package.json new file mode 100644 index 00000000..8428c3d2 --- /dev/null +++ b/packages/redux-devtools-app/assets/electron/package.json @@ -0,0 +1,9 @@ +{ + "name": "remotedev", + "productName": "RemoteDev", + "version": "0.0.1", + "electronVersion": "1.4.5", + "main": "index.js", + "description": "Remote Redux DevTools", + "authors": "Mihail Diordiev" +} diff --git a/packages/redux-devtools-app/assets/electron/resources/config.json b/packages/redux-devtools-app/assets/electron/resources/config.json new file mode 100644 index 00000000..cd8d618a --- /dev/null +++ b/packages/redux-devtools-app/assets/electron/resources/config.json @@ -0,0 +1,17 @@ +{ + "osx" : { + "title": "RemoteDev", + "background": "osx/installer.png", + "icon": "osx/icon.icns", + "icon-size": 225, + "contents": [ + { "x": 290, "y": 1, "type": "link", "path": "/Applications" }, + { "x": 1, "y": 1, "type": "file" } + ] + }, + "win" : { + "title" : "RemoteDev", + "version" : "0.0.0.1", + "icon" : "windows/icon.ico" + } +} diff --git a/packages/redux-devtools-app/assets/electron/resources/osx/icon.icns b/packages/redux-devtools-app/assets/electron/resources/osx/icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..bcce2de75d8bbdb1775f015958714577bba350eb GIT binary patch literal 50428 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 V)c$Sj#PxFQry( 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 literal 0 HcmV?d00001 diff --git a/packages/redux-devtools-app/assets/electron/resources/windows/icon.ico b/packages/redux-devtools-app/assets/electron/resources/windows/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a5fd48a60e9a59ae2876720757beb5be80f556c7 GIT binary patch literal 99678 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

Vh(`95$3Vlq^XcDUzPMeMO>-)e%oyg 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 + + + + RemoteDev + + + +

+ + \ No newline at end of file diff --git a/packages/redux-devtools-app/package.json b/packages/redux-devtools-app/package.json new file mode 100644 index 00000000..fa4d204d --- /dev/null +++ b/packages/redux-devtools-app/package.json @@ -0,0 +1,112 @@ +{ + "name": "remotedev-app", + "version": "0.11.0-3", + "description": "Remote Redux DevTools web, electron and chrome app.", + "scripts": { + "start": "webpack-dev-server --hot --inline --env.development --env.platform=web --progress", + "start:electron": "npm run build:electron && electron ./build/electron", + "build:electron": "rimraf ./build/electron && webpack -p --env.platform=electron --progress", + "build:web": "rimraf ./build/web && webpack -p --env.platform=web --progress", + "build:umd": "rimraf ./dist && webpack --env.development --progress --config webpack.config.umd.js", + "build:umd:min": "webpack -p --progress --config webpack.config.umd.js", + "clean": "rimraf ./build", + "build": "rimraf ./lib && babel ./src/app --out-dir lib", + "prepublish": "eslint ./src/app && npm run test && npm run build && npm run build:umd && npm run build:umd:min", + "lint": "eslint src", + "lint:fix": "eslint src --fix", + "test": "NODE_ENV=test jest --no-cache" + }, + "main": "lib/index.js", + "files": [ + "lib", + "dist" + ], + "jest": { + "moduleNameMapper": { + "\\.(css|scss)$": "/test/__mocks__/styleMock.js" + } + }, + "repository": { + "type": "git", + "url": "https://github.com/zalmoxisus/remotedev-app" + }, + "homepage": "https://github.com/zalmoxisus/remotedev-app", + "keywords": [ + "react", + "redux", + "devtools" + ], + "author": "Mihail Diordiev (https://github.com/zalmoxisus)", + "license": "MIT", + "devDependencies": { + "babel-cli": "^6.22.2", + "babel-core": "^6.22.1", + "babel-eslint": "^7.1.1", + "babel-loader": "^6.2.10", + "babel-plugin-add-module-exports": "^0.2.1", + "babel-plugin-react-transform": "^2.0.2", + "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-preset-env": "^1.1.8", + "babel-preset-es2015": "^6.22.0", + "babel-preset-react": "^6.22.0", + "babel-preset-stage-0": "^6.22.0", + "babel-register": "^6.22.0", + "chromedriver": "^2.20.0", + "copy-webpack-plugin": "^4.0.1", + "css-loader": "^0.26.1", + "electron": "^1.4.5", + "electron-builder": "^2.5.0", + "electron-debug": "^1.1.0", + "electron-packager": "^8.2.0", + "enzyme": "^2.8.2", + "enzyme-to-json": "^1.5.1", + "eslint": "^3.15.0", + "eslint-config-airbnb": "^14.1.0", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-jsx-a11y": "^4.0.0", + "eslint-plugin-react": "^6.9.0", + "file-loader": "^0.10.0", + "html-loader": "^0.4.4", + "html-webpack-plugin": "^2.28.0", + "jest": "^20.0.4", + "raw-loader": "^0.5.1", + "react": "^15.5.4", + "react-addons-test-utils": "15.4.0", + "react-dom": "^15.5.4", + "react-test-renderer": "^15.5.4", + "redux-immutable-state-invariant": "^1.2.0", + "redux-logger": "^2.2.1", + "rimraf": "^2.5.4", + "style-loader": "^0.13.0", + "url-loader": "^0.5.7", + "webpack": "^2.2.1", + "webpack-dev-server": "^2.3.0", + "webpack-hot-middleware": "^2.16.1" + }, + "dependencies": { + "d3-state-visualizer": "^1.3.1", + "devui": "^1.0.0-2", + "javascript-stringify": "^1.5.0", + "jsan": "^3.1.9", + "jsondiffpatch": "^0.2.4", + "localforage": "^1.5.0", + "lodash": "^4.0.0", + "prop-types": "^15.5.10", + "react-icons": "^2.2.5", + "react-redux": "^5.0.5", + "redux": "^3.0.5", + "redux-devtools": "^3.4.0", + "redux-devtools-chart-monitor": "^1.6.1", + "redux-devtools-instrument": "^1.8.0", + "redux-devtools-log-monitor": "^1.3.0", + "redux-devtools-test-generator": "^0.5.1", + "redux-persist": "^4.8.0", + "redux-slider-monitor": "^2.0.0-0", + "remotedev-inspector-monitor": "^0.11.0", + "socketcluster-client": "^5.5.0", + "styled-components": "^2.0.0" + }, + "peerDependencies": { + "react": "^15.4.0" + } +} diff --git a/packages/redux-devtools-app/src/app/actions/index.js b/packages/redux-devtools-app/src/app/actions/index.js new file mode 100644 index 00000000..1cdfb2cd --- /dev/null +++ b/packages/redux-devtools-app/src/app/actions/index.js @@ -0,0 +1,110 @@ +import { + CHANGE_SECTION, CHANGE_THEME, SELECT_INSTANCE, SELECT_MONITOR, UPDATE_MONITOR_STATE, + LIFTED_ACTION, MONITOR_ACTION, EXPORT, TOGGLE_SYNC, TOGGLE_SLIDER, TOGGLE_DISPATCHER, + TOGGLE_PERSIST, GET_REPORT_REQUEST, SHOW_NOTIFICATION, CLEAR_NOTIFICATION +} from '../constants/actionTypes'; +import { RECONNECT } from '../constants/socketActionTypes'; + +let monitorReducer; +let monitorProps = {}; + +export function changeSection(section) { + return { type: CHANGE_SECTION, section }; +} + +export function changeTheme(data) { + return { type: CHANGE_THEME, ...data.formData }; +} + +export function liftedDispatch(action) { + if (action.type[0] === '@') { + if (action.type === '@@INIT_MONITOR') { + monitorReducer = action.update; + monitorProps = action.monitorProps; + } + return { type: MONITOR_ACTION, action, monitorReducer, monitorProps }; + } + return { type: LIFTED_ACTION, message: 'DISPATCH', action }; +} + +export function selectInstance(selected) { + return { type: SELECT_INSTANCE, selected }; +} + +export function selectMonitor(monitor) { + return { type: SELECT_MONITOR, monitor }; +} + +export function selectMonitorWithState(value, monitorState) { + return { type: SELECT_MONITOR, monitor: value, monitorState }; +} + +export function selectMonitorTab(subTabName) { + return { type: UPDATE_MONITOR_STATE, nextState: { subTabName } }; +} + +export function updateMonitorState(nextState) { + return { type: UPDATE_MONITOR_STATE, nextState }; +} + +export function importState(state, preloadedState) { + return { type: LIFTED_ACTION, message: 'IMPORT', state, preloadedState }; +} + +export function exportState() { + return { type: EXPORT }; +} + +export function lockChanges(status) { + return { + type: LIFTED_ACTION, + message: 'DISPATCH', + action: { type: 'LOCK_CHANGES', status }, + toAll: true + }; +} + +export function pauseRecording(status) { + return { + type: LIFTED_ACTION, + message: 'DISPATCH', + action: { type: 'PAUSE_RECORDING', status }, + toAll: true + }; +} + +export function dispatchRemotely(action) { + return { type: LIFTED_ACTION, message: 'ACTION', action }; +} + +export function togglePersist() { + return { type: TOGGLE_PERSIST }; +} + +export function toggleSync() { + return { type: TOGGLE_SYNC }; +} + +export function toggleSlider() { + return { type: TOGGLE_SLIDER }; +} + +export function toggleDispatcher() { + return { type: TOGGLE_DISPATCHER }; +} + +export function saveSocketSettings(options) { + return { type: RECONNECT, options }; +} + +export function showNotification(message) { + return { type: SHOW_NOTIFICATION, notification: { type: 'error', message } }; +} + +export function clearNotification() { + return { type: CLEAR_NOTIFICATION }; +} + +export function getReport(report) { + return { type: GET_REPORT_REQUEST, report }; +} diff --git a/packages/redux-devtools-app/src/app/components/BottomButtons.js b/packages/redux-devtools-app/src/app/components/BottomButtons.js new file mode 100644 index 00000000..e9de2292 --- /dev/null +++ b/packages/redux-devtools-app/src/app/components/BottomButtons.js @@ -0,0 +1,56 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Button, Toolbar, Divider, Spacer } from 'devui'; +import SaveIcon from 'react-icons/lib/md/save'; +import ExportButton from './buttons/ExportButton'; +import ImportButton from './buttons/ImportButton'; +import PrintButton from './buttons/PrintButton'; +import DispatcherButton from './buttons/DispatcherButton'; +import SliderButton from './buttons/SliderButton'; +import MonitorSelector from './MonitorSelector'; + +export default class BottomButtons extends Component { + static propTypes = { + dispatcherIsOpen: PropTypes.bool, + sliderIsOpen: PropTypes.bool, + options: PropTypes.object.isRequired + }; + + shouldComponentUpdate(nextProps, nextState) { + return nextProps.dispatcherIsOpen !== this.props.dispatcherIsOpen + || nextProps.sliderIsOpen !== this.props.sliderIsOpen + || nextProps.options !== this.props.options; + } + + render() { + const features = this.props.options.features; + return ( + + {features.export && + + } + {features.export && + + } + {features.import && + + } + + + + + {features.jump && + + } + {features.dispatch && + + } + + ); + } +} diff --git a/packages/redux-devtools-app/src/app/components/Header.js b/packages/redux-devtools-app/src/app/components/Header.js new file mode 100644 index 00000000..f16c42c0 --- /dev/null +++ b/packages/redux-devtools-app/src/app/components/Header.js @@ -0,0 +1,78 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Tabs, Toolbar, Button, Divider, Spacer } from 'devui'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import DocsIcon from 'react-icons/lib/go/book'; +import FeedBackIcon from 'react-icons/lib/io/android-textsms'; +import TwitterIcon from 'react-icons/lib/ti/social-twitter'; +import SupportIcon from 'react-icons/lib/ti/heart-full-outline'; +import { changeSection } from '../actions'; + +const tabs = [ + { name: 'Actions' }, + { name: 'Reports' }, + { name: 'Settings' } +]; + +class Header extends Component { + static propTypes = { + section: PropTypes.string.isRequired, + changeSection: PropTypes.func.isRequired + }; + + openLink = url => () => { + window.open(url); + }; + + render() { + return ( + + + + + + + + + ); + } +} + +function mapDispatchToProps(dispatch) { + return { + changeSection: bindActionCreators(changeSection, dispatch) + }; +} + +export default connect(null, mapDispatchToProps)(Header); diff --git a/packages/redux-devtools-app/src/app/components/InstanceSelector.js b/packages/redux-devtools-app/src/app/components/InstanceSelector.js new file mode 100644 index 00000000..2bdf43c3 --- /dev/null +++ b/packages/redux-devtools-app/src/app/components/InstanceSelector.js @@ -0,0 +1,47 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { Select } from 'devui'; +import { selectInstance } from '../actions'; + +class InstanceSelector extends Component { + static propTypes = { + selected: PropTypes.string, + instances: PropTypes.object.isRequired, + onSelect: PropTypes.func.isRequired + }; + + render() { + this.select = [{ value: '', label: 'Autoselect instances' }]; + const instances = this.props.instances; + let name; + Object.keys(instances).forEach(key => { + name = instances[key].name; + if (name !== undefined) this.select.push({ value: key, label: instances[key].name }); + }); + + return ( + + + ); + } +} + +function mapDispatchToProps(dispatch) { + return { + importState: bindActionCreators(importState, dispatch) + }; +} + +export default connect(null, mapDispatchToProps)(ImportButton); diff --git a/packages/redux-devtools-app/src/app/components/buttons/LockButton.js b/packages/redux-devtools-app/src/app/components/buttons/LockButton.js new file mode 100644 index 00000000..673eb7ff --- /dev/null +++ b/packages/redux-devtools-app/src/app/components/buttons/LockButton.js @@ -0,0 +1,40 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { Button } from 'devui'; +import LockIcon from 'react-icons/lib/io/ios-locked'; +import { lockChanges } from '../../actions'; + +class LockButton extends Component { + static propTypes = { + locked: PropTypes.bool, + disabled: PropTypes.bool, + lockChanges: PropTypes.func.isRequired + }; + + shouldComponentUpdate(nextProps) { + return nextProps.locked !== this.props.locked; + } + + render() { + return ( + + ); + } +} + +function mapDispatchToProps(dispatch, ownProps) { + return { + lockChanges: () => dispatch(lockChanges(!ownProps.locked)) + }; +} + +export default connect(null, mapDispatchToProps)(LockButton); diff --git a/packages/redux-devtools-app/src/app/components/buttons/PersistButton.js b/packages/redux-devtools-app/src/app/components/buttons/PersistButton.js new file mode 100644 index 00000000..127b4cc8 --- /dev/null +++ b/packages/redux-devtools-app/src/app/components/buttons/PersistButton.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { Button } from 'devui'; +import PersistIcon from 'react-icons/lib/fa/thumb-tack'; +import { togglePersist } from '../../actions'; + +class LockButton extends Component { + static propTypes = { + persisted: PropTypes.bool, + disabled: PropTypes.bool, + onClick: PropTypes.func.isRequired + }; + + shouldComponentUpdate(nextProps) { + return nextProps.persisted !== this.props.persisted; + } + + render() { + return ( + + ); + } +} + +function mapStateToProps(state) { + return { + persisted: state.instances.persisted + }; +} + +function mapDispatchToProps(dispatch) { + return { + onClick: bindActionCreators(togglePersist, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(LockButton); diff --git a/packages/redux-devtools-app/src/app/components/buttons/PrintButton.js b/packages/redux-devtools-app/src/app/components/buttons/PrintButton.js new file mode 100644 index 00000000..662d7821 --- /dev/null +++ b/packages/redux-devtools-app/src/app/components/buttons/PrintButton.js @@ -0,0 +1,40 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Button } from 'devui'; +import PrintIcon from 'react-icons/lib/md/print'; + +export default class PrintButton extends Component { + shouldComponentUpdate() { + return false; + } + + handlePrint() { + const d3svg = document.getElementById('d3svg'); + if (!d3svg) { + window.print(); + return; + } + + const initHeight = d3svg.style.height; + const initWidth = d3svg.style.width; + const box = d3svg.getBBox(); + d3svg.style.height = box.height; + d3svg.style.width = box.width; + + const g = d3svg.firstChild; + const initTransform = g.getAttribute('transform'); + g.setAttribute('transform', initTransform.replace(/.+scale\(/, 'translate(57, 10) scale(')); + + window.print(); + + d3svg.style.height = initHeight; + d3svg.style.width = initWidth; + g.setAttribute('transform', initTransform); + } + + render() { + return ( + + ); + } +} diff --git a/packages/redux-devtools-app/src/app/components/buttons/RecordButton.js b/packages/redux-devtools-app/src/app/components/buttons/RecordButton.js new file mode 100644 index 00000000..a6da9128 --- /dev/null +++ b/packages/redux-devtools-app/src/app/components/buttons/RecordButton.js @@ -0,0 +1,38 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { Button } from 'devui'; +import RecordIcon from 'react-icons/lib/md/fiber-manual-record'; +import { pauseRecording } from '../../actions'; + +class RecordButton extends Component { + static propTypes = { + paused: PropTypes.bool, + pauseRecording: PropTypes.func.isRequired + }; + + shouldComponentUpdate(nextProps) { + return nextProps.paused !== this.props.paused; + } + + render() { + return ( + + ); + } +} + +function mapDispatchToProps(dispatch, ownProps) { + return { + pauseRecording: () => dispatch(pauseRecording(!ownProps.paused)) + }; +} + +export default connect(null, mapDispatchToProps)(RecordButton); diff --git a/packages/redux-devtools-app/src/app/components/buttons/SliderButton.js b/packages/redux-devtools-app/src/app/components/buttons/SliderButton.js new file mode 100644 index 00000000..b031f43d --- /dev/null +++ b/packages/redux-devtools-app/src/app/components/buttons/SliderButton.js @@ -0,0 +1,39 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { Button } from 'devui'; +import HistoryIcon from 'react-icons/lib/md/av-timer'; +import { toggleSlider } from '../../actions'; + +class SliderButton extends Component { + static propTypes = { + isOpen: PropTypes.bool, + toggleSlider: PropTypes.func.isRequired + }; + + shouldComponentUpdate(nextProps) { + return nextProps.isOpen !== this.props.isOpen; + } + + render() { + return ( + + ); + } +} + +function mapDispatchToProps(dispatch) { + return { + toggleSlider: bindActionCreators(toggleSlider, dispatch) + }; +} + +export default connect(null, mapDispatchToProps)(SliderButton); diff --git a/packages/redux-devtools-app/src/app/components/buttons/SyncButton.js b/packages/redux-devtools-app/src/app/components/buttons/SyncButton.js new file mode 100644 index 00000000..3d8c1062 --- /dev/null +++ b/packages/redux-devtools-app/src/app/components/buttons/SyncButton.js @@ -0,0 +1,45 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { Button } from 'devui'; +import SyncIcon from 'react-icons/lib/ti/arrow-sync'; +import { toggleSync } from '../../actions'; + +class SyncButton extends Component { + static propTypes = { + sync: PropTypes.bool, + onClick: PropTypes.func.isRequired + }; + + shouldComponentUpdate(nextProps) { + return nextProps.sync !== this.props.sync; + } + + render() { + return ( + + ); + } +} + +function mapStateToProps(state) { + return { + sync: state.instances.sync + }; +} + +function mapDispatchToProps(dispatch) { + return { + onClick: bindActionCreators(toggleSync, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(SyncButton); diff --git a/packages/redux-devtools-app/src/app/constants/actionTypes.js b/packages/redux-devtools-app/src/app/constants/actionTypes.js new file mode 100644 index 00000000..13898f66 --- /dev/null +++ b/packages/redux-devtools-app/src/app/constants/actionTypes.js @@ -0,0 +1,24 @@ +export const CHANGE_SECTION = 'main/CHANGE_SECTION'; +export const CHANGE_THEME = 'main/CHANGE_THEME'; + +export const UPDATE_STATE = 'devTools/UPDATE_STATE'; +export const SET_STATE = 'devTools/SET_STATE'; +export const SELECT_INSTANCE = 'devTools/SELECT_INSTANCE'; +export const REMOVE_INSTANCE = 'devTools/REMOVE_INSTANCE'; +export const LIFTED_ACTION = 'devTools/LIFTED_ACTION'; +export const MONITOR_ACTION = 'devTools/MONITOR_ACTION'; +export const TOGGLE_SYNC = 'devTools/TOGGLE_SYNC'; +export const TOGGLE_PERSIST = 'devTools/TOGGLE_PERSIST'; +export const SELECT_MONITOR = 'devTools/SELECT_MONITOR'; +export const UPDATE_MONITOR_STATE = 'devTools/UPDATE_MONITOR_STATE'; +export const TOGGLE_SLIDER = 'devTools/TOGGLE_SLIDER'; +export const TOGGLE_DISPATCHER = 'devTools/TOGGLE_DISPATCHER'; +export const EXPORT = 'devTools/EXPORT'; +export const SHOW_NOTIFICATION = 'devTools/SHOW_NOTIFICATION'; +export const CLEAR_NOTIFICATION = 'devTools/CLEAR_NOTIFICATION'; + +export const UPDATE_REPORTS = 'reports/UPDATE'; +export const GET_REPORT_REQUEST = 'reports/GET_REPORT_REQUEST'; +export const GET_REPORT_ERROR = 'reports/GET_REPORT_ERROR'; +export const GET_REPORT_SUCCESS = 'reports/GET_REPORT_SUCCESS'; +export const ERROR = 'ERROR'; diff --git a/packages/redux-devtools-app/src/app/constants/dataTypes.js b/packages/redux-devtools-app/src/app/constants/dataTypes.js new file mode 100644 index 00000000..a7c5b562 --- /dev/null +++ b/packages/redux-devtools-app/src/app/constants/dataTypes.js @@ -0,0 +1,2 @@ +export const DATA_TYPE_KEY = Symbol.for('__serializedType__'); +export const DATA_REF_KEY = Symbol.for('__serializedRef__'); diff --git a/packages/redux-devtools-app/src/app/constants/socketActionTypes.js b/packages/redux-devtools-app/src/app/constants/socketActionTypes.js new file mode 100644 index 00000000..1df28612 --- /dev/null +++ b/packages/redux-devtools-app/src/app/constants/socketActionTypes.js @@ -0,0 +1,19 @@ +import socketCluster from 'socketcluster-client'; + +export const { + CLOSED, CONNECTING, OPEN, AUTHENTICATED, PENDING, UNAUTHENTICATED + } = socketCluster.SCSocket; +export const CONNECT_REQUEST = 'socket/CONNECT_REQUEST'; +export const CONNECT_SUCCESS = 'socket/CONNECT_SUCCESS'; +export const CONNECT_ERROR = 'socket/CONNECT_ERROR'; +export const RECONNECT = 'socket/RECONNECT'; +export const AUTH_REQUEST = 'socket/AUTH_REQUEST'; +export const AUTH_SUCCESS = 'socket/AUTH_SUCCESS'; +export const AUTH_ERROR = 'socket/AUTH_ERROR'; +export const DISCONNECTED = 'socket/DISCONNECTED'; +export const DEAUTHENTICATE = 'socket/DEAUTHENTICATE'; +export const SUBSCRIBE_REQUEST = 'socket/SUBSCRIBE_REQUEST'; +export const SUBSCRIBE_SUCCESS = 'socket/SUBSCRIBE_SUCCESS'; +export const SUBSCRIBE_ERROR = 'socket/SUBSCRIBE_ERROR'; +export const UNSUBSCRIBE = 'socket/UNSUBSCRIBE'; +export const EMIT = 'socket/EMIT'; diff --git a/packages/redux-devtools-app/src/app/constants/socketOptions.js b/packages/redux-devtools-app/src/app/constants/socketOptions.js new file mode 100644 index 00000000..500d9a6a --- /dev/null +++ b/packages/redux-devtools-app/src/app/constants/socketOptions.js @@ -0,0 +1,12 @@ +const socketOptions = { + hostname: 'remotedev.io', + port: 443, + protocol: 'https', + autoReconnect: true, + secure: true, + autoReconnectOptions: { + randomness: 30000 + } +}; + +export default socketOptions; diff --git a/packages/redux-devtools-app/src/app/containers/Actions.js b/packages/redux-devtools-app/src/app/containers/Actions.js new file mode 100644 index 00000000..4f464b5c --- /dev/null +++ b/packages/redux-devtools-app/src/app/containers/Actions.js @@ -0,0 +1,80 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { Container } from 'devui'; +import SliderMonitor from './monitors/Slider'; +import { liftedDispatch as liftedDispatchAction, getReport } from '../actions'; +import { getActiveInstance } from '../reducers/instances'; +import DevTools from '../containers/DevTools'; +import Dispatcher from './monitors/Dispatcher'; +import TopButtons from '../components/TopButtons'; +import BottomButtons from '../components/BottomButtons'; + +class Actions extends Component { + render() { + const { + monitor, dispatcherIsOpen, sliderIsOpen, options, liftedState, liftedDispatch + } = this.props; + return ( + + + + {sliderIsOpen && options.connectionId && options.features.jump && + + } + {dispatcherIsOpen && options.connectionId && options.features.dispatch && + + } + + + ); + } +} + +Actions.propTypes = { + liftedDispatch: PropTypes.func.isRequired, + liftedState: PropTypes.object.isRequired, + monitorState: PropTypes.object, + options: PropTypes.object.isRequired, + monitor: PropTypes.string, + dispatcherIsOpen: PropTypes.bool, + sliderIsOpen: PropTypes.bool +}; + +function mapStateToProps(state) { + const instances = state.instances; + const id = getActiveInstance(instances); + return { + liftedState: instances.states[id], + monitorState: state.monitor.monitorState, + options: instances.options[id], + monitor: state.monitor.selected, + dispatcherIsOpen: state.monitor.dispatcherIsOpen, + sliderIsOpen: state.monitor.sliderIsOpen, + reports: state.reports.data + }; +} + +function mapDispatchToProps(dispatch) { + return { + liftedDispatch: bindActionCreators(liftedDispatchAction, dispatch), + getReport: bindActionCreators(getReport, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(Actions); diff --git a/packages/redux-devtools-app/src/app/containers/App.js b/packages/redux-devtools-app/src/app/containers/App.js new file mode 100644 index 00000000..80187e48 --- /dev/null +++ b/packages/redux-devtools-app/src/app/containers/App.js @@ -0,0 +1,58 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { Container, Notification } from 'devui'; +import { clearNotification } from '../actions'; +import Header from '../components/Header'; +import Actions from '../containers/Actions'; +import Settings from '../components/Settings'; + +class App extends Component { + render() { + const { section, theme, notification } = this.props; + let body; + switch (section) { + case 'Settings': body = ; break; + default: body = ; + } + + return ( + +
+ {body} + {notification && + + {notification.message} + + } + + ); + } +} + +App.propTypes = { + section: PropTypes.string.isRequired, + theme: PropTypes.object.isRequired, + notification: PropTypes.shape({ + message: PropTypes.string, + type: PropTypes.string + }), + clearNotification: PropTypes.func +}; + +function mapStateToProps(state) { + return { + section: state.section, + theme: state.theme, + notification: state.notification + }; +} + +function mapDispatchToProps(dispatch) { + return { + clearNotification: bindActionCreators(clearNotification, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(App); diff --git a/packages/redux-devtools-app/src/app/containers/DevTools.js b/packages/redux-devtools-app/src/app/containers/DevTools.js new file mode 100644 index 00000000..0b9730bf --- /dev/null +++ b/packages/redux-devtools-app/src/app/containers/DevTools.js @@ -0,0 +1,88 @@ +import React, { Component, PropTypes, createElement } from 'react'; +import { withTheme } from 'styled-components'; +import getMonitor from '../utils/getMonitor'; + +class DevTools extends Component { + constructor(props) { + super(props); + this.getMonitor(props, props.monitorState); + } + + getMonitor(props, skipUpdate) { + const monitorElement = getMonitor(props); + this.monitorProps = monitorElement.props; + this.Monitor = monitorElement.type; + + const update = this.Monitor.update; + if (update) { + let newMonitorState; + const monitorState = props.monitorState; + if (skipUpdate || monitorState && monitorState.__overwritten__ === props.monitor) { + newMonitorState = monitorState; + } else { + newMonitorState = update(this.monitorProps, undefined, {}); + if (newMonitorState !== monitorState) { + this.preventRender = true; + } + } + this.dispatch({ + type: '@@INIT_MONITOR', + newMonitorState, + update, + monitorProps: this.monitorProps + }); + } + } + + componentWillUpdate(nextProps) { + if (nextProps.monitor !== this.props.monitor) this.getMonitor(nextProps); + } + + shouldComponentUpdate(nextProps) { + return ( + nextProps.monitor !== this.props.monitor || + nextProps.liftedState !== this.props.liftedState || + nextProps.monitorState !== this.props.liftedState || + nextProps.features !== this.props.features || + nextProps.theme.scheme !== this.props.theme.scheme + ); + } + + dispatch = action => { + this.props.dispatch(action); + }; + + render() { + if (this.preventRender) { + this.preventRender = false; + return null; + } + + const liftedState = { + ...this.props.liftedState, + monitorState: this.props.monitorState + }; + return ( +
+ +
+ ); + } +} + +DevTools.propTypes = { + liftedState: PropTypes.object, + monitorState: PropTypes.object, + dispatch: PropTypes.func.isRequired, + monitor: PropTypes.string, + features: PropTypes.object.isRequired, + theme: PropTypes.object.isRequired +}; + +export default withTheme(DevTools); diff --git a/packages/redux-devtools-app/src/app/containers/monitors/ChartMonitorWrapper.js b/packages/redux-devtools-app/src/app/containers/monitors/ChartMonitorWrapper.js new file mode 100644 index 00000000..658fba66 --- /dev/null +++ b/packages/redux-devtools-app/src/app/containers/monitors/ChartMonitorWrapper.js @@ -0,0 +1,55 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import ChartMonitor from 'redux-devtools-chart-monitor'; +import { selectMonitorWithState } from '../../actions'; + +export function getPath(obj, inspectedStatePath) { + const parent = obj.parent; + if (!parent) return; + getPath(parent, inspectedStatePath); + let name = obj.name; + const item = name.match(/.+\[(\d+)]/); + if (item) name = item[1]; + inspectedStatePath.push(name); +} + +class ChartMonitorWrapper extends Component { + static update = ChartMonitor.update; + + onClickText = (data) => { + const inspectedStatePath = []; + getPath(data, inspectedStatePath); + this.props.selectMonitorWithState('InspectorMonitor', { + inspectedStatePath, + tabName: 'State', + subTabName: data.children ? 'Chart' : 'Tree', + selectedActionId: null, + startActionId: null, + inspectedActionPath: [] + }); + }; + + render() { + return ( + + ); + } +} + +ChartMonitorWrapper.propTypes = { + selectMonitorWithState: PropTypes.func.isRequired +}; + +function mapDispatchToProps(dispatch) { + return { + selectMonitorWithState: bindActionCreators(selectMonitorWithState, dispatch) + }; +} + +export default connect(null, mapDispatchToProps)(ChartMonitorWrapper); diff --git a/packages/redux-devtools-app/src/app/containers/monitors/Dispatcher.js b/packages/redux-devtools-app/src/app/containers/monitors/Dispatcher.js new file mode 100644 index 00000000..f725b803 --- /dev/null +++ b/packages/redux-devtools-app/src/app/containers/monitors/Dispatcher.js @@ -0,0 +1,201 @@ +// Based on https://github.com/YoruNoHikage/redux-devtools-dispatch + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; +import { Button, Select, Editor, Toolbar } from 'devui'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { dispatchRemotely } from '../../actions'; + +export const DispatcherContainer = styled.div` + display: flex; + flex-direction: column; + flex-shrink: 0; + padding-top: 2px; + background: ${props => props.theme.base01}; +`; + +export const CodeContainer = styled.div` + height: 75px; + padding-right: 6px; + overflow: auto; +`; + +export const ActionContainer = styled.div` + display: table; + width: 100%; + color: ${props => props.theme.base06}; + + > div { + display: table-row; + + > div:first-child { + width: 1px; + padding-left: 8px; + display: table-cell; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + + > div:nth-child(2) { + display: table-cell; + width: 100%; + padding: 6px; + } + } +`; + +class Dispatcher extends Component { + static propTypes = { + options: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired + }; + + state = { + selected: 'default', + customAction: this.props.options.lib === 'redux' ? '{\n type: \'\'\n}' : 'this.', + args: [], + rest: '[]', + changed: false + }; + + componentWillReceiveProps(nextProps) { + if (this.state.selected !== 'default' && !nextProps.options.actionCreators) { + this.setState({ + selected: 'default', + args: [] + }); + } + } + + shouldComponentUpdate(nextProps, nextState) { + return nextState !== this.state || + nextProps.options.actionCreators !== this.props.options.actionCreators; + } + + selectActionCreator = selected => { + if (selected === 'actions-help') { + window.open('https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/' + + 'basics/Dispatcher.md'); + return; + } + + const args = []; + if (selected !== 'default') { + args.length = this.props.options.actionCreators[selected].args.length; + } + this.setState({ selected, args, rest: '[]', changed: false }); + }; + + handleArg = argIndex => value => { + const args = [ + ...this.state.args.slice(0, argIndex), + value || undefined, + ...this.state.args.slice(argIndex + 1), + ]; + this.setState({ args, changed: true }); + }; + + handleRest = rest => { + this.setState({ rest, changed: true }); + }; + + handleCustomAction = customAction => { + this.setState({ customAction, changed: true }); + }; + + dispatchAction = () => { + const { selected, customAction, args, rest } = this.state; + + if (this.state.selected !== 'default') { + // remove trailing `undefined` arguments + let i = args.length - 1; + while (i >= 0 && typeof args[i] === 'undefined') { + args.pop(i); i--; + } + this.props.dispatch({ + name: this.props.options.actionCreators[selected].name, + selected, + args, + rest + }); + } else { + this.props.dispatch(customAction); + } + this.setState({ changed: false }); + }; + + render() { + const actionCreators = this.props.options.actionCreators; + let actionElement; + + if (this.state.selected === 'default' || !actionCreators) { + actionElement = ( + + + + ); + } else { + actionElement = ( + + {actionCreators[this.state.selected].args.map((param, i) => ( +
+
{param}
+ +
+ ))} +
+
...rest
+ +
+
+ ); + } + + let options = [{ value: 'default', label: 'Custom action' }]; + if (actionCreators && actionCreators.length > 0) { + options = options.concat(actionCreators.map(({ name, func, args }, i) => ({ + value: i, + label: `${name}(${args.join(', ')})` + }))); + } else { + options.push({ value: 'actions-help', label: 'Add your app built-in actions…' }); + } + + return ( + + {actionElement} + +