diff --git a/.eslintrc.js b/.eslintrc.js index f1070507..4921e395 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,6 +24,7 @@ module.exports = { ], globals: { + fetch: true, window: true, document: true, describe: true, diff --git a/packages/xod-client-browser/webpack.config.js b/packages/xod-client-browser/webpack.config.js index 08ab974d..ccf37aa6 100644 --- a/packages/xod-client-browser/webpack.config.js +++ b/packages/xod-client-browser/webpack.config.js @@ -96,6 +96,7 @@ module.exports = { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.XOD_HM_DEF': JSON.stringify(process.env.XOD_HM_DEF || false), + 'process.env.XOD_HOSTNAME': JSON.stringify(process.env.XOD_HOSTNAME || 'xod.io'), 'process.env.XOD_SITE_DOMAIN': JSON.stringify(''), 'process.env.XOD_FORUM_DOMAIN': JSON.stringify('https://forum.xod.io/'), 'process.env.XOD_UTM_SOURCE': JSON.stringify('ide-browser'), diff --git a/packages/xod-client-electron/src/upload/components/PopupUploadConfig.jsx b/packages/xod-client-electron/src/upload/components/PopupUploadConfig.jsx index d339c05a..a783dd18 100644 --- a/packages/xod-client-electron/src/upload/components/PopupUploadConfig.jsx +++ b/packages/xod-client-electron/src/upload/components/PopupUploadConfig.jsx @@ -41,6 +41,7 @@ class PopupUploadConfig extends React.Component { this.getSelectedBoard() .then(selectedBoard => this.getBoards(selectedBoard)); this.getPorts(); + this.props.updateCompileLimit(); } onClose() { @@ -252,6 +253,7 @@ class PopupUploadConfig extends React.Component { render() { const boards = this.renderBoardSelect(); const ports = this.renderPortSelect(); + const compileLimitLeft = this.props.compileLimitLeft; return (
@@ -300,6 +306,8 @@ class PopupUploadConfig extends React.Component { PopupUploadConfig.propTypes = { isVisible: PropTypes.bool, selectedPort: PropTypes.object, + compileLimitLeft: PropTypes.number, + updateCompileLimit: PropTypes.func, getSelectedBoard: PropTypes.func, listBoards: PropTypes.func, listPorts: PropTypes.func, diff --git a/packages/xod-client-electron/src/view/containers/App.jsx b/packages/xod-client-electron/src/view/containers/App.jsx index 9b2b455d..6ab68cda 100644 --- a/packages/xod-client-electron/src/view/containers/App.jsx +++ b/packages/xod-client-electron/src/view/containers/App.jsx @@ -227,6 +227,7 @@ class App extends client.App { } // Remove listener if process is finished. ipcRenderer.removeAllListeners(UPLOAD_TO_ARDUINO); + this.props.actions.updateCompileLimit(); }); } @@ -549,6 +550,8 @@ class App extends client.App { selectedPort={this.props.selectedPort} listBoards={this.listBoards} listPorts={this.listPorts} + compileLimitLeft={this.props.compileLimitLeft} + updateCompileLimit={this.props.actions.updateCompileLimit} onBoardChanged={this.onArduinoTargetBoardChange} onPortChanged={this.onSerialPortChange} onUpload={this.onUploadToArduino} @@ -658,6 +661,7 @@ App.propTypes = R.merge(client.App.propTypes, { project: client.sanctuaryPropType(Project), actions: PropTypes.objectOf(PropTypes.func), upload: PropTypes.object, + compileLimitLeft: PropTypes.number, workspace: PropTypes.string, selectedPort: PropTypes.object, }); @@ -674,6 +678,7 @@ const mapStateToProps = R.applySpec({ saveProcess: getSaveProcess, currentPatchPath: client.getCurrentPatchPath, selectedPort: getSelectedSerialPort, + compileLimitLeft: client.getCompileLimitLeft, popups: { createProject: client.getPopupVisibility(client.POPUP_ID.CREATING_PROJECT), projectSelection: client.getPopupVisibility(client.POPUP_ID.OPENING_PROJECT), diff --git a/packages/xod-client-electron/webpack.config.js b/packages/xod-client-electron/webpack.config.js index c401f151..dbfef6f3 100644 --- a/packages/xod-client-electron/webpack.config.js +++ b/packages/xod-client-electron/webpack.config.js @@ -110,6 +110,7 @@ const options = { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.XOD_HM_DEF': JSON.stringify(process.env.XOD_HM_DEF || false), + 'process.env.XOD_HOSTNAME': JSON.stringify(process.env.XOD_HOSTNAME || 'xod.io'), 'process.env.XOD_SITE_DOMAIN': JSON.stringify('https://xod.io/'), 'process.env.XOD_FORUM_DOMAIN': JSON.stringify('https://forum.xod.io/'), 'process.env.XOD_UTM_SOURCE': JSON.stringify('ide-desktop'), diff --git a/packages/xod-client/README.md b/packages/xod-client/README.md index 4b13d8d3..74ae762b 100644 --- a/packages/xod-client/README.md +++ b/packages/xod-client/README.md @@ -35,3 +35,9 @@ is used. Reducers simply delegate state update to `xod-project`’s functions, selectors, and components use functions from `xod-project` to access project data. This distincion is done because project state is complex and keeping all machinery inside standard pod layout would make it messy. + +## Environment variables + +- **`XOD_HOSTNAME`** + + XOD hostname. Default: `xod.io` diff --git a/packages/xod-client/package.json b/packages/xod-client/package.json index 424283a3..83b9c45a 100644 --- a/packages/xod-client/package.json +++ b/packages/xod-client/package.json @@ -23,6 +23,7 @@ "font-awesome": "^4.6.3", "formsy-react": "^0.18.1", "formsy-react-components": "^0.8.1", + "isomorphic-fetch": "^2.2.1", "prop-types": "^15.5.10", "ramda": "^0.24.1", "ramda-fantasy": "^0.7.0", diff --git a/packages/xod-client/src/core/actions.js b/packages/xod-client/src/core/actions.js index 702763c5..d4aedd0c 100644 --- a/packages/xod-client/src/core/actions.js +++ b/packages/xod-client/src/core/actions.js @@ -22,6 +22,7 @@ export const showCode = code => ({ payload: { code }, }); +export * from '../user/actions'; export * from '../editor/actions'; export * from '../project/actions'; export * from '../projectBrowser/actions'; diff --git a/packages/xod-client/src/core/containers/App.jsx b/packages/xod-client/src/core/containers/App.jsx index f5ad7b91..a63e465d 100644 --- a/packages/xod-client/src/core/containers/App.jsx +++ b/packages/xod-client/src/core/containers/App.jsx @@ -29,6 +29,7 @@ export default class App extends React.Component { document.addEventListener('cut', this.props.actions.cutEntities); document.addEventListener('copy', this.props.actions.copyEntities); document.addEventListener('paste', this.props.actions.pasteEntities); + this.props.actions.updateCompileLimit(); } onShowCodeArduino() { @@ -128,6 +129,7 @@ App.propTypes = { popups: PropTypes.objectOf(PropTypes.bool), popupsData: PropTypes.objectOf(PropTypes.object), actions: PropTypes.shape({ + updateCompileLimit: PropTypes.func.isRequired, createProject: PropTypes.func.isRequired, updateProjectMeta: PropTypes.func.isRequired, hideAllPopups: PropTypes.func.isRequired, @@ -163,6 +165,7 @@ App.propTypes = { }; App.actions = { + updateCompileLimit: actions.updateCompileLimit, createProject: actions.createProject, requestCreateProject: actions.requestCreateProject, requestRenameProject: actions.requestRenameProject, diff --git a/packages/xod-client/src/core/reducer.js b/packages/xod-client/src/core/reducer.js index 117c38f9..31953536 100644 --- a/packages/xod-client/src/core/reducer.js +++ b/packages/xod-client/src/core/reducer.js @@ -1,6 +1,7 @@ import { merge } from 'ramda'; import { combineReducers } from 'redux'; +import userReducer from '../user/reducer'; import projectReducer from '../project/reducer'; import undoableProject from './undoableProject'; import projectBrowserReducer from '../projectBrowser/reducer'; @@ -15,6 +16,7 @@ import keepIntegrityAfterNavigatingHistory from './keepIntegrityAfterNavigatingH const combineRootReducers = (extraReducers) => { const reducers = merge( { + user: userReducer, project: projectReducer, projectHistory: (s = {}) => s, projectBrowser: projectBrowserReducer, diff --git a/packages/xod-client/src/core/selectors.js b/packages/xod-client/src/core/selectors.js index 0d59fdf5..14bb56f5 100644 --- a/packages/xod-client/src/core/selectors.js +++ b/packages/xod-client/src/core/selectors.js @@ -4,6 +4,7 @@ import { createSelector } from 'reselect'; import * as XP from 'xod-project'; +import * as User from '../user/selectors'; import * as Editor from '../editor/selectors'; import * as Project from '../project/selectors'; import * as ProjectBrowser from '../projectBrowser/selectors'; @@ -70,6 +71,7 @@ export const getPatchForHelpbar = createSelector( export default { + User, Editor, Errors, Processes, diff --git a/packages/xod-client/src/core/state.js b/packages/xod-client/src/core/state.js index a685f754..72cdabf4 100644 --- a/packages/xod-client/src/core/state.js +++ b/packages/xod-client/src/core/state.js @@ -1,9 +1,11 @@ +import userState from '../user/state'; import editorState from '../editor/state'; import projectState from '../project/state'; import debuggerState from '../debugger/state'; import projectBrowserState from '../projectBrowser/state'; export default { + user: userState, project: projectState, projectHistory: {}, projectBrowser: projectBrowserState, diff --git a/packages/xod-client/src/editor/components/NodeInspector.jsx b/packages/xod-client/src/editor/components/NodeInspector.jsx index 45924a01..34e3e78d 100644 --- a/packages/xod-client/src/editor/components/NodeInspector.jsx +++ b/packages/xod-client/src/editor/components/NodeInspector.jsx @@ -14,7 +14,7 @@ import Widgets, { WIDGET_MAPPING } from './inspectorWidgets'; import { RenderableNode } from '../../types'; import sanctuaryPropType from '../../utils/sanctuaryPropType'; -import { getUtmSiteUrl } from '../../utils/siteLinks'; +import { getUtmSiteUrl } from '../../utils/urls'; import * as MESSAGES from '../messages'; diff --git a/packages/xod-client/src/index.js b/packages/xod-client/src/index.js index b932dd81..f6e56779 100644 --- a/packages/xod-client/src/index.js +++ b/packages/xod-client/src/index.js @@ -1,3 +1,4 @@ +import * as UserSelectors from './user/selectors'; import * as EditorSelectors from './editor/selectors'; import * as UtilsSelectors from './utils/selectors'; import * as ProcessSelectors from './processes/selectors'; @@ -22,7 +23,7 @@ import * as PopupConstants from './popups/constants'; import popupsReducer, { showOnlyPopup, hideOnePopup } from './popups/reducer'; -import * as siteLinkUtils from './utils/siteLinks'; +import * as siteLinkUtils from './utils/urls'; import * as BrowserUtils from './utils/browser'; import * as MenuUtils from './utils/menu'; import sanctuaryPropType from './utils/sanctuaryPropType'; @@ -63,7 +64,7 @@ export * from './debugger/selectors'; export * from './utils/browser'; export * from './utils/constants'; -export * from './utils/siteLinks'; +export * from './utils/urls'; export * from './popups/constants'; export { lowercaseKebabMask } from './utils/inputFormatting'; export { default as sanctuaryPropType } from './utils/sanctuaryPropType'; @@ -109,6 +110,7 @@ export default Object.assign({ PopupProjectPreferences, TAB_CLOSE, }, + UserSelectors, UtilsSelectors, EditorSelectors, ProcessSelectors, diff --git a/packages/xod-client/src/projectBrowser/containers/ProjectBrowser.jsx b/packages/xod-client/src/projectBrowser/containers/ProjectBrowser.jsx index 7123a66f..786406ad 100644 --- a/packages/xod-client/src/projectBrowser/containers/ProjectBrowser.jsx +++ b/packages/xod-client/src/projectBrowser/containers/ProjectBrowser.jsx @@ -36,7 +36,7 @@ import PatchTypeSelector from '../components/PatchTypeSelector'; import ProjectBrowserPopups from '../components/ProjectBrowserPopups'; import ProjectBrowserToolbar from '../components/ProjectBrowserToolbar'; -import { getUtmSiteUrl } from '../../utils/siteLinks'; +import { getUtmSiteUrl } from '../../utils/urls'; import { IconGuide } from '../../utils/components/IconGuide'; const PATCH_TYPE = { diff --git a/packages/xod-client/src/user/actionTypes.js b/packages/xod-client/src/user/actionTypes.js new file mode 100644 index 00000000..475553ea --- /dev/null +++ b/packages/xod-client/src/user/actionTypes.js @@ -0,0 +1,2 @@ +export const UPDATE_COMPILE_LIMIT = 'UPDATE_COMPILE_LIMIT'; +export default {}; diff --git a/packages/xod-client/src/user/actions.js b/packages/xod-client/src/user/actions.js new file mode 100644 index 00000000..f9797ed4 --- /dev/null +++ b/packages/xod-client/src/user/actions.js @@ -0,0 +1,12 @@ +import { getCompileLimitUrl } from '../utils/urls'; +import { UPDATE_COMPILE_LIMIT } from './actionTypes'; + +export const updateCompileLimit = () => dispatch => + fetch(getCompileLimitUrl()) + .then(res => (res.ok ? res.json() : null)) + .catch(() => null) + .then(limit => dispatch({ + type: UPDATE_COMPILE_LIMIT, + payload: limit, + })); +export default {}; diff --git a/packages/xod-client/src/user/reducer.js b/packages/xod-client/src/user/reducer.js new file mode 100644 index 00000000..9b73b456 --- /dev/null +++ b/packages/xod-client/src/user/reducer.js @@ -0,0 +1,14 @@ +import { UPDATE_COMPILE_LIMIT } from './actionTypes'; + +const userReducer = (state = {}, action) => { + switch (action.type) { + case UPDATE_COMPILE_LIMIT: { + const limit = action.payload; + return { ...state, limit }; + } + default: + return state; + } +}; + +export default userReducer; diff --git a/packages/xod-client/src/user/selectors.js b/packages/xod-client/src/user/selectors.js new file mode 100644 index 00000000..152103d8 --- /dev/null +++ b/packages/xod-client/src/user/selectors.js @@ -0,0 +1,14 @@ +import R from 'ramda'; +import { createSelector } from 'reselect'; + +export const getUserState = R.prop('user'); + +export const getCompileLimit = createSelector( + getUserState, + R.prop('limit') +); + +export const getCompileLimitLeft = createSelector( + getCompileLimit, + limit => (limit ? limit.limit - limit.pending - limit.used : null) +); diff --git a/packages/xod-client/src/user/state.js b/packages/xod-client/src/user/state.js new file mode 100644 index 00000000..92948718 --- /dev/null +++ b/packages/xod-client/src/user/state.js @@ -0,0 +1,3 @@ +export default { + limit: null, +}; diff --git a/packages/xod-client/src/utils/siteLinks.js b/packages/xod-client/src/utils/urls.js similarity index 86% rename from packages/xod-client/src/utils/siteLinks.js rename to packages/xod-client/src/utils/urls.js index c34f0911..93c51eee 100644 --- a/packages/xod-client/src/utils/siteLinks.js +++ b/packages/xod-client/src/utils/urls.js @@ -29,3 +29,9 @@ export const getUtmSiteUrl = getUtmUrl(process.env.XOD_SITE_DOMAIN); */ // :: String -> String export const getUtmForumUrl = getUtmUrl(process.env.XOD_FORUM_DOMAIN, '', 'forum'); + +const HOSTNAME = process.env.XOD_HOSTNAME || 'xod.io'; + +// :: String -> String +export const getCompileLimitUrl = () => + `https://compile.${HOSTNAME}/limits`; diff --git a/yarn.lock b/yarn.lock index 6d7bbbbe..3e8e4602 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5778,7 +5778,7 @@ isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" -isomorphic-fetch@2.2.1, isomorphic-fetch@^2.1.1: +isomorphic-fetch@2.2.1, isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" dependencies: