diff --git a/packages/xod-client-electron/src/index.jsx b/packages/xod-client-electron/src/index.jsx
index 1b747726..212f2787 100644
--- a/packages/xod-client-electron/src/index.jsx
+++ b/packages/xod-client-electron/src/index.jsx
@@ -8,6 +8,7 @@ import popupsReducer from './popups/reducer';
import uploadReducer from './upload/reducer';
import debuggerMiddleware from './debugger/middleware';
+import autoupdateMiddleware from './view/autoupdateMiddleware';
import installLibMiddleware from './view/installLibMiddleware';
const extraReducers = {
@@ -18,6 +19,7 @@ const extraReducers = {
const extraMiddlewares = [
debuggerMiddleware,
installLibMiddleware,
+ autoupdateMiddleware,
];
ReactDOM.render(
diff --git a/packages/xod-client-electron/src/shared/composeMessage.js b/packages/xod-client-electron/src/shared/composeMessage.js
new file mode 100644
index 00000000..10441641
--- /dev/null
+++ b/packages/xod-client-electron/src/shared/composeMessage.js
@@ -0,0 +1,8 @@
+// Duplicate of `composeMessage` from `xod-client`
+// It's necessary to prevent errors with importing `xod-client`,
+// that contains styles and react components.
+export default (title, note = null, button = null) => ({
+ title,
+ note,
+ button,
+});
diff --git a/packages/xod-client-electron/src/shared/errorFormatter.js b/packages/xod-client-electron/src/shared/errorFormatter.js
index 13acecb0..60b26990 100644
--- a/packages/xod-client-electron/src/shared/errorFormatter.js
+++ b/packages/xod-client-electron/src/shared/errorFormatter.js
@@ -5,42 +5,95 @@ import { ERROR_CODES as XFS_EC } from 'xod-fs';
// that contains async/await functions and non-isomorphic dependencies
import { COMPILATION_ERRORS as XD_EC } from 'xod-deploy/dist/constants';
+import composeMessage from './composeMessage';
import * as EC from './errorCodes';
-const UNKNOWN_ERROR = err => `Unknown error occurred: ${err.message || JSON.stringify(err)}`;
+const UNKNOWN_ERROR = err => composeMessage(
+ 'Unknown error occurred',
+ err.message || JSON.stringify(err)
+);
const ERROR_FORMATTERS = {
+ // Errors that will showed in the upload popup
[EC.TRANSPILE_ERROR]: err => `Error occurred during transpilation: ${err}`,
-
[EC.PORT_NOT_FOUND]: err => `Could not find Arduino device on port: ${err.port.comName}. Available ports: ${R.map(R.prop('comName'), err.ports)}`,
[EC.UPLOAD_ERROR]: err => `Error occured during uploading: ${err}`,
[EC.INDEX_LIST_ERROR]: err => `Could not connect to Arduino Packages Index at ${err.request.path}: ${err.error.message}`,
[EC.CANT_INSTALL_ARCHITECTURE]: err => `Could not install tools: ${err}`,
- [XFS_EC.INVALID_WORKSPACE_PATH]: err => `Invalid workspace path: "${err.path}" of type "${typeof err.path}"`,
- [XFS_EC.WORKSPACE_DIR_NOT_EMPTY]: err => `Workspace directory at ${err.path} is not empty`,
- [XFS_EC.WORKSPACE_DIR_NOT_EXIST_OR_EMPTY]: err => `Workspace directory at ${err.path} not exist or empty`,
+ // Snackbar errors
+ [XFS_EC.INVALID_WORKSPACE_PATH]: err => composeMessage(
+ 'Invalid workspace path',
+ `"${err.path}" of type "${typeof err.path}"`
+ ),
+ [XFS_EC.WORKSPACE_DIR_NOT_EMPTY]: err => composeMessage(
+ `Workspace directory at ${err.path} is not empty`
+ ),
+ [XFS_EC.WORKSPACE_DIR_NOT_EXIST_OR_EMPTY]: err => composeMessage(
+ `Workspace directory at ${err.path} not exist or empty`
+ ),
- [XFS_EC.INVALID_FILE_CONTENTS]: err => `Could not open selected project: invalid contents in ${err.path}`,
+ [XFS_EC.INVALID_FILE_CONTENTS]: err => composeMessage(
+ 'Canʼt open selected project',
+ `Invalid contents in ${err.path}`
+ ),
- [XFS_EC.CANT_CREATE_WORKSPACE_FILE]: err => `Could not create workspace at ${err.path}: ${err.message}`,
- [XFS_EC.CANT_COPY_STDLIB]: err => `Could not copy stdlib at ${err.path}: ${err.message}`,
- [XFS_EC.CANT_COPY_DEFAULT_PROJECT]: err => `Could not copy default project at ${err.path}: ${err.message}`,
- [XFS_EC.CANT_ENUMERATE_PROJECTS]: err => `Could not enumerate projects at ${err.path}: ${err.message}`,
- [XFS_EC.CANT_SAVE_PROJECT]: err => `Could not save the project at ${err.path}: ${err.message}`,
- [XFS_EC.CANT_SAVE_LIBRARY]: err => `Could not save the library at ${err.path}: ${err.message}`,
+ [XFS_EC.CANT_CREATE_WORKSPACE_FILE]: err => composeMessage(
+ `Could not create workspace at ${err.path}`,
+ err.message
+ ),
+ [XFS_EC.CANT_COPY_STDLIB]: err => composeMessage(
+ `Could not copy stdlib at ${err.path}`,
+ err.message
+ ),
+ [XFS_EC.CANT_COPY_DEFAULT_PROJECT]: err => composeMessage(
+ `Could not copy default project at ${err.path}`,
+ err.message
+ ),
+ [XFS_EC.CANT_ENUMERATE_PROJECTS]: err => composeMessage(
+ `Could not enumerate projects at ${err.path}`,
+ err.message
+ ),
+ [XFS_EC.CANT_SAVE_PROJECT]: err => composeMessage(
+ `Could not save the project at ${err.path}`,
+ err.message
+ ),
+ [XFS_EC.CANT_SAVE_LIBRARY]: err => composeMessage(
+ `Could not save the library at ${err.path}`,
+ err.message
+ ),
- [EC.CANT_CREATE_NEW_PROJECT]: err => `Could not create a new project: ${err.message}`,
- [EC.CANT_OPEN_SELECTED_PROJECT]: err => `Could not open selected project: ${err.message}`,
+ [EC.CANT_CREATE_NEW_PROJECT]: err => composeMessage(
+ 'Could not create a new project',
+ err.message
+ ),
+ [EC.CANT_OPEN_SELECTED_PROJECT]: err => composeMessage(
+ 'Could not open selected project',
+ err.message
+ ),
[EC.BOARD_NOT_SUPPORTED]: err => `Board ${err.boardId} is not supported yet`,
[EC.CANT_GET_UPLOAD_CONFIG]: err => `Could not get upload config for board ${err.boardId}. Returned status ${err.status} ${err.statusText}`,
- [EC.CLOUD_NETWORK_ERROR]: err => `Could not connect to cloud service. Probably you donʼt have an internet connection. Error: ${err.message}`,
+ [EC.CLOUD_NETWORK_ERROR]: err => composeMessage(
+ 'Could not connect to cloud service.',
+ `Probably you donʼt have an internet connection. Error: ${err.message}`
+ ),
- [XD_EC.COMPILE_FAILED]: err => `Compilation failed with error: ${err.message}`,
- [XD_EC.COMPILE_TIMEDOUT]: err => `Compilation timed out (${err.message}ms expired)`,
- [XD_EC.CLOSED]: err => `Connection was closed with reason (${err.closeCode}) ${err.reason}`,
- [XD_EC.FAILED]: err => `Canʼt establish connection with compile server: (${err.code}) ${err.message}`,
+ [XD_EC.COMPILE_FAILED]: err => composeMessage(
+ 'Compilation failed with error',
+ err.message
+ ),
+ [XD_EC.COMPILE_TIMEDOUT]: err => composeMessage(
+ `Compilation timed out (${err.message}ms expired)`
+ ),
+ [XD_EC.CLOSED]: err => composeMessage(
+ `Connection was closed with reason (${err.closeCode})`,
+ err.reason
+ ),
+ [XD_EC.FAILED]: err => composeMessage(
+ `Canʼt establish connection with compile server: (${err.code})`,
+ err.message
+ ),
};
// :: Error -> String
diff --git a/packages/xod-client-electron/src/shared/messages.js b/packages/xod-client-electron/src/shared/messages.js
index 10b8bea7..ca3ef06d 100644
--- a/packages/xod-client-electron/src/shared/messages.js
+++ b/packages/xod-client-electron/src/shared/messages.js
@@ -1,3 +1,4 @@
+import composeMessage from './composeMessage';
import * as EVENTS from './events';
export const CODE_TRANSPILED = 'Project was successfully transpiled. Searching for device...';
@@ -18,6 +19,18 @@ export const SAVE_ALL_PROCESSED = '';
export const SAVE_ALL_FAILED = 'Failed to save project.';
export const SAVE_ALL_SUCCEED = 'Saved successfully!';
-export const DEBUG_SESSION_STOPPED_ON_CHANGE = 'Debug session was automatically stopped, cause your Project has been changed.';
-export const DEBUG_SESSION_STOPPED_ON_TAB_CLOSE = 'Debug session was automatically stopped, cause you closed Debugger tab.';
+export const DEBUG_SESSION_STOPPED_ON_CHANGE = composeMessage(
+ 'Debug session stopped',
+ 'Your Project has been changed.'
+);
+export const DEBUG_SESSION_STOPPED_ON_TAB_CLOSE = composeMessage(
+ 'Debug session stopped',
+ 'You closed Debugger tab.'
+);
export const DEBUG_LOST_CONNECTION = 'Lost connection with the device.';
+
+export const updateAvailableMessage = version => composeMessage(
+ 'Update available',
+ `New version ${version} of XOD\u00A0IDE is available`,
+ 'Download & Install'
+);
diff --git a/packages/xod-client-electron/src/view/actions.js b/packages/xod-client-electron/src/view/actions.js
index aab3f520..f101d00d 100644
--- a/packages/xod-client-electron/src/view/actions.js
+++ b/packages/xod-client-electron/src/view/actions.js
@@ -10,6 +10,7 @@ import {
addConfirmation,
addError,
SAVE_ALL,
+ composeMessage,
} from 'xod-client';
import * as EVENTS from '../shared/events';
import * as MESSAGES from '../shared/messages';
@@ -91,7 +92,9 @@ export const createAsyncAction = ({
);
}
if (notify) {
- dispatch(addConfirmation(completeMsg));
+ dispatch(addConfirmation(
+ composeMessage(completeMsg)
+ ));
}
onComplete(payload, dispatch);
@@ -119,7 +122,7 @@ export const createAsyncAction = ({
);
}
if (notify) {
- dispatch(addError(errorMsg));
+ dispatch(addError(composeMessage(errorMsg)));
}
onError(err, dispatch);
diff --git a/packages/xod-client-electron/src/view/autoupdate.js b/packages/xod-client-electron/src/view/autoupdate.js
index cebfc804..35766461 100644
--- a/packages/xod-client-electron/src/view/autoupdate.js
+++ b/packages/xod-client-electron/src/view/autoupdate.js
@@ -1,5 +1,8 @@
import R from 'ramda';
import * as EVENTS from '../shared/events';
+import { updateAvailableMessage } from '../shared/messages';
+
+export const UPDATE_IDE_MESSAGE_ID = 'updateIde';
// :: ipcRenderer -> AppContainer -> ()
export const subscribeAutoUpdaterEvents = (ipcRenderer, App) => {
@@ -15,15 +18,9 @@ export const subscribeAutoUpdaterEvents = (ipcRenderer, App) => {
(event, info) => {
console.log('Update available: ', info); // eslint-disable-line no-console
App.props.actions.addNotification(
- `New version (${info.version}) of XOD\u00A0IDE is available`,
- [{
- id: 'downloadAndInstall',
- text: 'Download & Install',
- }, {
- id: 'dismiss',
- text: 'Skip',
- }],
- true
+ updateAvailableMessage(info.version),
+ true,
+ UPDATE_IDE_MESSAGE_ID
);
}
);
@@ -42,6 +39,7 @@ export const subscribeAutoUpdaterEvents = (ipcRenderer, App) => {
ipcRenderer.on(
EVENTS.APP_UPDATE_DOWNLOAD_STARTED,
() => {
+ App.setState(R.assoc('downloadProgressPopup', true));
console.log('Download has been started!'); // eslint-disable-line no-console
}
);
diff --git a/packages/xod-client-electron/src/view/autoupdateMiddleware.js b/packages/xod-client-electron/src/view/autoupdateMiddleware.js
new file mode 100644
index 00000000..bb85f7fb
--- /dev/null
+++ b/packages/xod-client-electron/src/view/autoupdateMiddleware.js
@@ -0,0 +1,12 @@
+import client from 'xod-client';
+import { ipcRenderer } from 'electron';
+
+import { UPDATE_IDE_MESSAGE_ID, downloadUpdate } from './autoupdate';
+
+export default () => next => (action) => {
+ if (action.type === client.MESSAGE_BUTTON_CLICKED && action.payload === UPDATE_IDE_MESSAGE_ID) {
+ downloadUpdate(ipcRenderer);
+ }
+
+ return next(action);
+};
diff --git a/packages/xod-client-electron/src/view/containers/App.jsx b/packages/xod-client-electron/src/view/containers/App.jsx
index b6a21dee..68db7308 100644
--- a/packages/xod-client-electron/src/view/containers/App.jsx
+++ b/packages/xod-client-electron/src/view/containers/App.jsx
@@ -37,7 +37,7 @@ import * as EVENTS from '../../shared/events';
import * as MESSAGES from '../../shared/messages';
import { createSystemMessage } from '../../shared/debuggerMessages';
-import { subscribeAutoUpdaterEvents, downloadUpdate } from '../autoupdate';
+import { subscribeAutoUpdaterEvents } from '../autoupdate';
import { subscribeToTriggerMainMenuRequests } from '../../testUtils/triggerMainMenu';
const { app, dialog, Menu } = remoteElectron;
@@ -85,8 +85,6 @@ class App extends client.App {
this.getSidebarPaneHeight = this.getSidebarPaneHeight.bind(this);
this.setSidebarPaneHeight = this.setSidebarPaneHeight.bind(this);
- this.onClickMessageButton = this.onClickMessageButton.bind(this);
-
this.onUploadToArduinoClicked = this.onUploadToArduinoClicked.bind(this);
this.onUploadToArduino = this.onUploadToArduino.bind(this);
this.onArduinoTargetBoardChange = this.onArduinoTargetBoardChange.bind(this);
@@ -184,13 +182,6 @@ class App extends client.App {
props.actions.fetchGrant();
}
- onClickMessageButton(buttonId, /* messageInfo */) {
- if (buttonId === 'downloadAndInstall') {
- downloadUpdate(ipcRenderer);
- this.setState(R.assoc('downloadProgressPopup', true));
- }
- }
-
onResize() {
this.setState(
R.set(
@@ -238,7 +229,9 @@ class App extends client.App {
proc.success(payload.message);
if (debug) {
foldEither(
- error => this.props.actions.addError(error.message),
+ error => this.props.actions.addError(
+ client.composeMessage(error.message)
+ ),
(nodeIdsMap) => {
this.props.actions.startDebuggerSession(
createSystemMessage('Debug session started'),
@@ -271,7 +264,7 @@ class App extends client.App {
this.props.actions.createProject(projectName);
ipcRenderer.send(EVENTS.CREATE_PROJECT, projectName);
ipcRenderer.once(EVENTS.SAVE_ALL, () => {
- this.props.actions.addConfirmation(MESSAGES.PROJECT_SAVE_SUCCEED);
+ this.props.actions.addConfirmation(MESSAGES.SAVE_ALL_SUCCEED);
});
}
@@ -303,7 +296,9 @@ class App extends client.App {
fs.readFile(filePaths[0], 'utf8', (err, data) => {
if (err) {
- this.props.actions.addError(err.message);
+ this.props.actions.addError(
+ client.composeMessage(err.message)
+ );
}
this.onImport(data);
@@ -337,7 +332,10 @@ class App extends client.App {
onImport(jsonString) {
foldEither(
- this.props.actions.addError,
+ R.compose(
+ this.props.actions.addError,
+ client.composeMessage
+ ),
(project) => {
if (!this.confirmUnsavedChanges()) return;
this.props.actions.importProject(project);
@@ -680,9 +678,6 @@ class App extends client.App {
setSidebarPaneHeight={this.setSidebarPaneHeight}
stopDebuggerSession={() => debuggerIPC.sendStopDebuggerSession(ipcRenderer)}
/>
-
{this.renderPopupShowCode()}
{this.renderPopupUploadConfig()}
{this.renderPopupUploadProcess()}
diff --git a/packages/xod-client/src/core/components/CloseButton.jsx b/packages/xod-client/src/core/components/CloseButton.jsx
new file mode 100644
index 00000000..22c0789b
--- /dev/null
+++ b/packages/xod-client/src/core/components/CloseButton.jsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import cn from 'classnames';
+import PropTypes from 'prop-types';
+
+const CloseButton = ({
+ as = 'button',
+ className,
+ ...restProps
+}) => React.createElement(
+ as,
+ {
+ ...restProps,
+ className: cn('CloseButton', className),
+ role: as !== 'button' ? 'button' : restProps.role,
+ }
+);
+
+CloseButton.propTypes = {
+ as: PropTypes.string,
+ className: PropTypes.string,
+};
+
+export default CloseButton;
diff --git a/packages/xod-client/src/core/containers/App.jsx b/packages/xod-client/src/core/containers/App.jsx
index 2703ab33..ce416d59 100644
--- a/packages/xod-client/src/core/containers/App.jsx
+++ b/packages/xod-client/src/core/containers/App.jsx
@@ -23,6 +23,7 @@ import PopupProjectPreferences from '../../project/components/PopupProjectPrefer
import PopupPublishProject from '../../project/components/PopupPublishProject';
import * as actions from '../actions';
+import composeMessage from '../../messages/composeMessage';
export default class App extends React.Component {
componentDidMount() {
@@ -37,7 +38,7 @@ export default class App extends React.Component {
const eitherCode = eitherTProject.map(transpile);
return foldEither(
- error => this.props.actions.addError(error.message),
+ error => this.props.actions.addError(composeMessage(error.message)),
this.props.actions.showCode,
eitherCode
);
diff --git a/packages/xod-client/src/core/styles/abstracts/colors.scss b/packages/xod-client/src/core/styles/abstracts/colors.scss
new file mode 100644
index 00000000..d4472d87
--- /dev/null
+++ b/packages/xod-client/src/core/styles/abstracts/colors.scss
@@ -0,0 +1,117 @@
+// Monochromes
+$black: #000;
+$white: #fff;
+
+$coal: #222;
+$coal-light: #252525;
+$coal-bright: #2b2b2b;
+
+$dark: #363636;
+$dark-deep: #303030;
+$dark-light: #3e3e3e;
+$dark-bright: #404040;
+
+$grey: #444;
+$grey-light: #555;
+$grey-bright: #666;
+
+$light-grey: #777;
+$light-grey-bright: #999;
+
+$chalk: #ccc;
+$chalk-light: #aaa;
+$chalk-bright: #eee;
+
+// Colored
+$blue: #2d83b4;
+$magenta: #9f5497;
+$cyan: #4ed5ed;
+$yellow: #f6dd0d;
+
+$red: #c33d3d;
+$red-bright: #fd6161;
+
+$green: #579205;
+$green-bright: #86cc23;
+
+$violet: #6965bd;
+$violet-light: #7570cb;
+$violet-shadow: #5c59a6;
+
+$orange: #c76530;
+$orange-light: #d9733c;
+$orange-shadow: #af592a;
+
+$blackberry: #4a4a57;
+$blackberry-light: #616077;
+$blackberry-shadow: #8c4a85;
+
+$olive: #827a22;
+$olive-light: #988f34;
+$olive-shadow: #726b1e;
+
+$emerald: #2e8f6c;
+$emerald-light: #3fa37e;
+$emerald-shadow: #287e5f;
+
+$apricot: #d8a53a;
+$apricot-light: #e9b445;
+$apricot-shadow: #be9133;
+
+$brown: #584b22;
+$brown-light: #8e5f35;
+$brown-shadow: #69421e;
+
+// Associated colors
+$selected: $cyan;
+$error: $red;
+
+$chrome-bg: $dark;
+$chrome-title: $coal-bright;
+$chrome-title-button-hover: $dark;
+$chrome-outlines: $coal;
+
+$chrome-light-bg: $chalk;
+
+$menu-bg: $dark-deep;
+$menu-hover: $dark-bright;
+$menu-active: $coal;
+$menu-item-bg: $coal;
+$menu-item-hover: $dark-deep;
+$menu-separator: $grey;
+
+$tab-bg: $coal;
+$tab-item-bg: $dark-deep;
+$tab-item-hover: $grey;
+$tab-button-hover: $dark;
+
+$canvas: $dark-deep;
+$canvas-outlines: $dark-light;
+
+$node-label: $chalk;
+$node-outlines: $light-grey-bright;
+$node-hover-outlines: $chalk;
+$pin-bg: $coal-light;
+$pin-label: $light-grey-bright;
+
+$input-bg: $chrome-bg;
+$input-outlines: $light-grey;
+$input-disabled-outlines: $dark-bright;
+$input-disabled-text: $grey-light;
+$input-active-bg: $grey-light;
+
+$dark-button-text: $coal;
+$dark-button-bg: $chrome-bg;
+$dark-button-outlines: $input-outlines;
+$dark-button-hover-bg: $tab-item-hover;
+$dark-button-hover-outlines: $grey-light;
+$dark-button-disabled-text: $grey;
+$dark-button-disabled-outlines: $grey;
+
+$light-button-text: $coal;
+$light-button-bg: $chrome-light-bg;
+$light-button-outlines: $input-outlines;
+$light-button-hover-bg: $light-button-bg;
+$light-button-hover-outlines: $grey-bright;
+$light-button-disabled-text: rgba($light-button-text, .5);
+$light-button-disabled-outlines: rgba($light-button-outlines, .5);
diff --git a/packages/xod-client/src/core/styles/abstracts/variables.scss b/packages/xod-client/src/core/styles/abstracts/variables.scss
index f19587c3..505de756 100644
--- a/packages/xod-client/src/core/styles/abstracts/variables.scss
+++ b/packages/xod-client/src/core/styles/abstracts/variables.scss
@@ -57,7 +57,6 @@ $color-tabs-hover-text: #FFF;
$color-tabs-debugger-background: #3d4931;
$color-tabs-debugger-text: #82b948;
-$snackbar-width: 220px;
$sidebar-width: 200px;
$sidebar-color-bg: #3d3d3d;
@@ -93,7 +92,3 @@ $button-fg-color-light: black;
$modal-fg-color: #cccccc;
$modal-fg-color-light: black;
$modal-bg-color-light: #cccccc;
-
-$message-border-color: #818181;
-$message-text-color: #e5e5e5;
-$message-bg-color: #3d3d3d;
diff --git a/packages/xod-client/src/core/styles/components/CloseButton.scss b/packages/xod-client/src/core/styles/components/CloseButton.scss
new file mode 100644
index 00000000..bfd80edf
--- /dev/null
+++ b/packages/xod-client/src/core/styles/components/CloseButton.scss
@@ -0,0 +1,52 @@
+.CloseButton {
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 32px;
+ height: 32px;
+
+ background: transparent;
+ border: 0;
+
+ cursor: pointer;
+
+ line-height: 0;
+ font-size: $font-size-xs;
+ color: $chalk;
+
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ outline: none;
+
+ &:hover, &:focus {
+ outline: none;
+
+ &:before {
+ background-color: $black;
+ }
+ }
+
+ &:before {
+ content: '';
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ width: 18px;
+ height: 18px;
+ background-color: rgba($black, .5);
+ border-radius: 100%;
+ border: 0;
+ }
+
+ &:after {
+ content: '╳';
+ display: inline;
+ position: absolute;
+ top: 15px;
+ width: 18px;
+ right: 6px;
+ z-index: 2;
+ }
+}
diff --git a/packages/xod-client/src/core/styles/components/SnackBarList.scss b/packages/xod-client/src/core/styles/components/SnackBarList.scss
index 5327dcca..87cdd406 100644
--- a/packages/xod-client/src/core/styles/components/SnackBarList.scss
+++ b/packages/xod-client/src/core/styles/components/SnackBarList.scss
@@ -1,8 +1,10 @@
.SnackBarList {
- position: fixed;
+ position: absolute;
z-index: 9999;
- top: 37px; // Height of a top bar. TODO: variable?
- right: 7px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ width: 100%;
margin: 0;
- width: $snackbar-width;
+ padding: 0;
}
diff --git a/packages/xod-client/src/core/styles/components/SnackBarMessage.scss b/packages/xod-client/src/core/styles/components/SnackBarMessage.scss
index 8b1bd290..0fd2f1aa 100644
--- a/packages/xod-client/src/core/styles/components/SnackBarMessage.scss
+++ b/packages/xod-client/src/core/styles/components/SnackBarMessage.scss
@@ -1,58 +1,82 @@
.SnackBarMessage {
position: relative;
display: none;
- margin-top: 7px;
- transition: all 0.3s ease-in;
- left: 0;
+ transition: all 0.1s ease-in;
+ opacity: 1;
- &.hidden {
- left: $snackbar-width;
- }
+ font-family: $font-family-normal;
+ font-weight: 300;
+ background: $chrome-bg;
+ color: $chalk;
+ border-top: 1px solid $chrome-outlines;
+ min-height: 60px;
+ box-sizing: border-box;
- &.display {
- display: block;
- }
+ &.hidden { opacity: 0; }
+ &.display { display: block; }
- &.error {
- a {
- color: $color-error;
- border-color: $color-error;
- }
- }
-
- &.confirmation {
- a {
- border-color: $message-border-color;
- }
- }
-
- &.notification {
- a {
- border-color: $color-success;
- }
- }
-
- a {
- display: block;
- padding: 11px 11px;
- color: $message-text-color;
- background-color: $message-bg-color;
- box-shadow: 0 0 7px rgba(0,0,0,.5);
-
- border: 2px solid $message-border-color;
- border-radius: 6px;
+ .message-content {
+ display: flex;
+ width: 70%;
+ padding: 10px 0;
+ margin: 0 auto;
cursor: default;
outline: none;
-
- .message-content { margin: 0; }
}
-
- .SnackBar-buttons-container {
- margin-top: 7px;
+ .message-text {
+ width: 65%;
+ margin: 0;
+ color: $white;
+ font-size: $font-size-m;
+ line-height: 1.4em;
+ padding-right: 3em;
+ opacity: 0.9;
+ }
+ .message-buttons {
+ width: 35%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
.Button {
- margin-right: 5px;
+ display: block;
+ width: 100%;
+ border: none;
+ }
+ }
+
+ .title {
+ display: block;
+ font-family: $font-family-condensed;
+ font-size: $font-size-l;
+ font-weight: 400;
+ margin-bottom: .5em;
+ }
+
+ &.persistent {
+ color: $white !important;
+ .title { color: $white !important; }
+ }
+
+ &.error {
+ .title { color: $red-bright; }
+
+ &.persistent { background: $error; }
+ }
+
+ &.confirmation {
+ .title { color: $green-bright; }
+
+ &.persistent {
+ background: $green;
+ }
+ }
+ &.notification {
+ .title { color: $cyan; }
+
+ &.persistent {
+ background: $blue;
}
}
}
diff --git a/packages/xod-client/src/core/styles/main.scss b/packages/xod-client/src/core/styles/main.scss
index 6deae435..827028f3 100644
--- a/packages/xod-client/src/core/styles/main.scss
+++ b/packages/xod-client/src/core/styles/main.scss
@@ -1,4 +1,5 @@
@import
+ 'abstracts/colors',
'abstracts/variables',
'abstracts/functions',
'abstracts/mixins';
@@ -18,6 +19,7 @@
'components/BackgroundLayer',
'components/Breadcrumbs',
'components/Button',
+ 'components/CloseButton',
'components/Comment',
'components/CppImplementationEditor',
'components/Debugger',
diff --git a/packages/xod-client/src/editor/actions.js b/packages/xod-client/src/editor/actions.js
index 4ed5bfa1..71d72098 100644
--- a/packages/xod-client/src/editor/actions.js
+++ b/packages/xod-client/src/editor/actions.js
@@ -7,11 +7,13 @@ import { fetchLibsWithDependencies, stringifyLibQuery, getLibName } from 'xod-pm
import {
SELECTION_ENTITY_TYPE,
CLIPBOARD_DATA_TYPE,
- CLIPBOARD_ERRORS as CLIPBOARD_ERROR_CODES,
} from './constants';
-import { LINK_ERRORS, CLIPBOARD_ERRORS } from '../messages/constants';
-
-import { libInstalled } from './messages';
+import { LINK_ERRORS } from '../project/messages';
+import {
+ libInstalled,
+ CLIPBOARD_RECURSION_PASTE_ERROR,
+ clipboardMissingPatchPasteError,
+} from './messages';
import * as ActionType from './actionTypes';
@@ -25,6 +27,7 @@ import {
addError,
addConfirmation,
} from '../messages/actions';
+import composeMessage from '../messages/composeMessage';
import * as Selectors from './selectors';
import * as ProjectSelectors from '../project/selectors';
@@ -345,7 +348,7 @@ export const pasteEntities = event => (dispatch, getState) => {
const pastedNodeTypes = R.map(XP.getNodeType, copiedEntities.nodes);
if (R.contains(currentPatchPath, pastedNodeTypes)) {
- dispatch(addError(CLIPBOARD_ERRORS[CLIPBOARD_ERROR_CODES.RECURSION_DETECTED]));
+ dispatch(addError(CLIPBOARD_RECURSION_PASTE_ERROR));
return;
}
@@ -358,10 +361,9 @@ export const pasteEntities = event => (dispatch, getState) => {
const missingPatches = R.without(availablePatches, pastedNodeTypes);
if (!R.isEmpty(missingPatches)) {
- dispatch(addError(XP.formatString(
- CLIPBOARD_ERRORS[CLIPBOARD_ERROR_CODES.NO_REQUIRED_PATCHES],
- { missingPatches: missingPatches.join(', ') }
- )));
+ dispatch(addError(
+ clipboardMissingPatchPasteError(missingPatches.join(', '))
+ ));
return;
}
@@ -472,6 +474,6 @@ export const installLibraries = libParams => (dispatch, getState) => {
errorCode: err.errorCode,
},
});
- dispatch(addError(err.message));
+ dispatch(addError(composeMessage(err.message)));
});
};
diff --git a/packages/xod-client/src/editor/constants.js b/packages/xod-client/src/editor/constants.js
index 9b0d1a55..f2b71c0f 100644
--- a/packages/xod-client/src/editor/constants.js
+++ b/packages/xod-client/src/editor/constants.js
@@ -48,10 +48,6 @@ export const LINK_ERRORS = {
INCOMPATIBLE_TYPES: 'INCOMPATIBLE_TYPES',
};
-export const PROPERTY_ERRORS = {
- PIN_HAS_LINK: 'PIN_HAS_LINK',
-};
-
export const CLIPBOARD_ERRORS = {
RECURSION_DETECTED: 'RECURSION_DETECTED',
NO_REQUIRED_PATCHES: 'NO_REQUIRED_PATCHES',
diff --git a/packages/xod-client/src/editor/containers/Editor.jsx b/packages/xod-client/src/editor/containers/Editor.jsx
index 0cc0f290..b5ad26a5 100644
--- a/packages/xod-client/src/editor/containers/Editor.jsx
+++ b/packages/xod-client/src/editor/containers/Editor.jsx
@@ -33,6 +33,7 @@ import Debugger from '../../debugger/containers/Debugger';
import Breadcrumbs from '../../debugger/containers/Breadcrumbs';
import Sidebar from '../components/Sidebar';
import Workarea from '../../utils/components/Workarea';
+import SnackBar from '../../messages/containers/SnackBar';
import { RenderableSelection } from '../../types';
import sanctuaryPropType from '../../utils/sanctuaryPropType';
@@ -235,6 +236,7 @@ class Editor extends React.Component {
{BreadcrumbsContainer}
{this.renderOpenedImplementationEditorTabs()}
{DebuggerContainer}
+
diff --git a/packages/xod-client/src/editor/messages.js b/packages/xod-client/src/editor/messages.js
index 18689fc9..956b09d9 100644
--- a/packages/xod-client/src/editor/messages.js
+++ b/packages/xod-client/src/editor/messages.js
@@ -1,6 +1,18 @@
+import composeMessage from '../messages/composeMessage';
+
export const PATCH_FOR_NODE_IS_MISSING = 'Patch for this node is missing.';
-export const libInstalled = (libName, version) => `${libName} @ ${version} installed successfully`;
+export const libInstalled = (libName, version) => composeMessage(
+ `${libName} @ ${version} installed successfully`
+);
+
+export const CLIPBOARD_RECURSION_PASTE_ERROR = composeMessage(
+ 'Canʼt paste a patch into itself'
+);
+export const clipboardMissingPatchPasteError = missingPatches => composeMessage(
+ 'Canʼt paste',
+ `Canʼt find following patches: ${missingPatches}`
+);
export const LIB_SUGGESTER_TYPE_TO_BEGIN = 'Type owner/libname to find a library';
export const LIB_SUGGESTER_NOTHING_FOUND = 'No library found';
diff --git a/packages/xod-client/src/index.js b/packages/xod-client/src/index.js
index d6f5fbab..1c58fd8e 100644
--- a/packages/xod-client/src/index.js
+++ b/packages/xod-client/src/index.js
@@ -15,6 +15,7 @@ import * as ProjectBrowserActions from './projectBrowser/actions';
import * as PopupActions from './popups/actions';
import * as DebuggerActions from './debugger/actions';
+import { MESSAGE_BUTTON_CLICKED } from './messages/actionTypes';
import { TAB_CLOSE, INSTALL_LIBRARIES_COMPLETE } from './editor/actionTypes';
import { SAVE_ALL } from './project/actionTypes';
@@ -33,6 +34,7 @@ import App from './core/containers/App';
import Root from './core/containers/Root';
import { container as Editor, CreateNodeWidget } from './editor';
import SnackBar from './messages';
+import composeMessage from './messages/composeMessage';
import * as MessageConstants from './messages/constants';
import Toolbar from './utils/components/Toolbar';
import PopupShowCode from './utils/components/PopupShowCode';
@@ -53,6 +55,7 @@ export * from './projectBrowser/actions';
export * from './popups/actions';
export * from './debugger/actions';
+export { MESSAGE_BUTTON_CLICKED } from './messages/actionTypes';
export { TAB_CLOSE, INSTALL_LIBRARIES_COMPLETE } from './editor/actionTypes';
export { SAVE_ALL } from './project/actionTypes';
@@ -82,6 +85,7 @@ export { default as App } from './core/containers/App';
export { default as Root } from './core/containers/Root';
export { container as Editor, CreateNodeWidget } from './editor';
export { default as SnackBar } from './messages';
+export { default as composeMessage } from './messages/composeMessage';
export * from './messages/constants';
export { default as initialState } from './core/state';
@@ -109,9 +113,11 @@ export default Object.assign({
PopupProjectPreferences,
hasUnsavedChanges,
getLastSavedProject,
+ composeMessage,
TAB_CLOSE,
SAVE_ALL,
INSTALL_LIBRARIES_COMPLETE,
+ MESSAGE_BUTTON_CLICKED,
},
UserSelectors,
EditorSelectors,
diff --git a/packages/xod-client/src/messages/actionTypes.js b/packages/xod-client/src/messages/actionTypes.js
index 36184a2c..5ec6ed45 100644
--- a/packages/xod-client/src/messages/actionTypes.js
+++ b/packages/xod-client/src/messages/actionTypes.js
@@ -1,2 +1,3 @@
export const MESSAGE_ADD = 'MESSAGE_ADD';
export const MESSAGE_DELETE = 'MESSAGE_DELETE';
+export const MESSAGE_BUTTON_CLICKED = 'MESSAGE_BUTTON_CLICKED';
diff --git a/packages/xod-client/src/messages/actions.js b/packages/xod-client/src/messages/actions.js
index 122a3172..b30561ee 100644
--- a/packages/xod-client/src/messages/actions.js
+++ b/packages/xod-client/src/messages/actions.js
@@ -4,10 +4,11 @@ import { STATUS } from '../utils/constants';
const getTimestamp = () => new Date().getTime();
-export const addMessage = (type, message, buttons = [], persistent = false) => ({
+export const addMessage = (type, messageData, persistent = false, id = null) => ({
type: ActionType.MESSAGE_ADD,
- payload: { message, buttons },
+ payload: messageData,
meta: {
+ id,
type,
persistent,
timestamp: getTimestamp(),
@@ -29,3 +30,8 @@ export const deleteMessage = id => ({
export const addError = (...args) => addMessage(MESSAGE_TYPE.ERROR, ...args);
export const addConfirmation = (...args) => addMessage(MESSAGE_TYPE.CONFIRMATION, ...args);
export const addNotification = (...args) => addMessage(MESSAGE_TYPE.NOTIFICATION, ...args);
+
+export const messageButtonClick = messageId => ({
+ type: ActionType.MESSAGE_BUTTON_CLICKED,
+ payload: messageId,
+});
diff --git a/packages/xod-client/src/messages/components/SnackBarMessage.jsx b/packages/xod-client/src/messages/components/SnackBarMessage.jsx
index 8d3b88b2..ceb61343 100644
--- a/packages/xod-client/src/messages/components/SnackBarMessage.jsx
+++ b/packages/xod-client/src/messages/components/SnackBarMessage.jsx
@@ -3,8 +3,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { MESSAGE_TYPE } from '../constants';
+import Button from '../../core/components/Button';
+import CloseButton from '../../core/components/CloseButton';
-const ANIMATION_TIMEOUT = 500;
+const ANIMATION_TIMEOUT = 100;
class SnackBarMessage extends React.Component {
constructor(props) {
@@ -16,6 +18,7 @@ class SnackBarMessage extends React.Component {
};
this.hide = this.hide.bind(this);
+ this.onCloseMessage = this.onCloseMessage.bind(this);
}
componentDidMount() {
@@ -24,35 +27,37 @@ class SnackBarMessage extends React.Component {
});
}
+ onCloseMessage() {
+ this.props.onCloseMessage(this.props.message.id);
+ }
+
getMessageContent() {
const { message, onClickMessageButton } = this.props;
- const buttons = R.unless(
- R.isEmpty,
- R.compose(
- btns => React.createElement(
- 'div',
- { className: 'SnackBar-buttons-container' },
- btns
- ),
- R.map(({ id, text }) => (
-
- ))
+ const button = R.unless(
+ R.isNil,
+ text => (
+
)
- )(message.payload.buttons);
+ )(message.payload.button);
- return (
-
- {message.payload.message}
- {buttons}
-
- );
+ return [
+
+
+ {message.payload.title}
+
+ {message.payload.note}
+
,
+
+ {button}
+
,
+ ];
}
setHidden(val) {
@@ -84,6 +89,7 @@ class SnackBarMessage extends React.Component {
error: message.type === MESSAGE_TYPE.ERROR,
confirmation: message.type === MESSAGE_TYPE.CONFIRMATION,
notification: message.type === MESSAGE_TYPE.NOTIFICATION,
+ persistent: message.persistent,
});
const messageContent = this.getMessageContent();
@@ -91,7 +97,11 @@ class SnackBarMessage extends React.Component {
-
+
+
{messageContent}
@@ -102,19 +112,21 @@ class SnackBarMessage extends React.Component {
SnackBarMessage.propTypes = {
message: PropTypes.shape({
/* eslint-disable react/no-unused-prop-types */
- id: PropTypes.number,
+ id: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ ]),
type: PropTypes.string,
persistent: PropTypes.bool,
payload: PropTypes.shape({
- message: PropTypes.string.isRequired,
- buttons: PropTypes.arrayOf(PropTypes.shape({
- id: PropTypes.string,
- text: PropTypes.string,
- })).isRequired,
+ title: PropTypes.string.isRequired,
+ note: PropTypes.string,
+ button: PropTypes.string,
}),
/* eslint-enable react/no-unused-prop-types */
}),
onClickMessageButton: PropTypes.func,
+ onCloseMessage: PropTypes.func.isRequired,
};
SnackBarMessage.defaultProps = {
diff --git a/packages/xod-client/src/messages/composeMessage.js b/packages/xod-client/src/messages/composeMessage.js
new file mode 100644
index 00000000..ace7f620
--- /dev/null
+++ b/packages/xod-client/src/messages/composeMessage.js
@@ -0,0 +1,5 @@
+export default (title, note = null, button = null) => ({
+ title,
+ note,
+ button,
+});
diff --git a/packages/xod-client/src/messages/constants.js b/packages/xod-client/src/messages/constants.js
index d5682395..a9c00d4b 100644
--- a/packages/xod-client/src/messages/constants.js
+++ b/packages/xod-client/src/messages/constants.js
@@ -1,51 +1,6 @@
-import {
- LINK_ERRORS as LE,
- NODETYPE_ERROR_TYPES as NTE,
- PROPERTY_ERRORS as PE,
- CLIPBOARD_ERRORS as CE,
-} from '../editor/constants';
-
+// eslint-disable-next-line import/prefer-default-export
export const MESSAGE_TYPE = {
ERROR: 'ERROR',
CONFIRMATION: 'CONFIRMATION',
NOTIFICATION: 'NOTIFICATION',
};
-
-export const LINK_ERRORS = {
- [LE.SAME_DIRECTION]: 'Can`t create link between pins of the same direction!',
- [LE.SAME_NODE]: 'Can`t create link between pins of the same node!',
- [LE.ONE_LINK_FOR_INPUT_PIN]: 'Input pin can have only one link!',
- [LE.UNKNOWN_ERROR]: 'Unknown error!',
- [LE.INCOMPATIBLE_TYPES]: 'Incompatible pin types!',
-};
-
-export const PROJECT_BROWSER_ERRORS = {
- CANT_OPEN_LIBPATCH_WITHOUT_XOD_IMPL: 'This patch has only native implementation and can`t be opened',
- CANT_DELETE_CURRENT_PATCH: 'Current patch cannot been deleted. Switch to another patch before!',
- PATCH_NAME_TAKEN: 'This patch name is already taken!',
- INVALID_PATCH_NAME: 'Invalid patch name',
- INVALID_PROJECT_NAME: 'Invalid project name',
-};
-
-export const NODETYPE_ERRORS = {
- [NTE.CANT_DELETE_USED_PATCHNODE]: (
- 'Current Patch Node is used somewhere. You should remove it first!'
- ),
- [NTE.CANT_DELETE_USED_PIN_OF_PATCHNODE]: [
- 'Current IO Node is represents a Pin of Patch Node.',
- 'And it is used somewhere.',
- 'You should remove a linkage first!',
- ].join(' '),
-};
-
-export const PROPERTY_ERRORS = {
- [PE.PIN_HAS_LINK]: [
- 'Can`t convert a pin into property, because it has a connected links.',
- 'You should remove links first!',
- ].join(' '),
-};
-
-export const CLIPBOARD_ERRORS = {
- [CE.RECURSION_DETECTED]: 'Can`t paste a patch into itself!',
- [CE.NO_REQUIRED_PATCHES]: 'Can`t find following patches: {missingPatches}',
-};
diff --git a/packages/xod-client/src/messages/containers/SnackBar.jsx b/packages/xod-client/src/messages/containers/SnackBar.jsx
index 1ddb7cfa..faecb426 100644
--- a/packages/xod-client/src/messages/containers/SnackBar.jsx
+++ b/packages/xod-client/src/messages/containers/SnackBar.jsx
@@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import SnackBarList from '../components/SnackBarList';
import SnackBarMessage from '../components/SnackBarMessage';
import * as ErrorSelectors from '../selectors';
-import { deleteMessage } from '../actions';
+import { deleteMessage, messageButtonClick } from '../actions';
const ERROR_TIMEOUT = 3000;
@@ -24,6 +24,7 @@ class SnackBar extends React.Component {
this.onMouseOver = this.onMouseOver.bind(this);
this.onMouseOut = this.onMouseOut.bind(this);
this.onButtonClicked = this.onButtonClicked.bind(this);
+ this.onCloseMessage = this.onCloseMessage.bind(this);
}
componentWillReceiveProps(nextProps) {
@@ -48,9 +49,13 @@ class SnackBar extends React.Component {
)(this.messages);
}
- onButtonClicked(buttonId, messageData) {
- this.props.onClickMessageButton(buttonId, messageData);
- this.hideMessage(messageData.id);
+ onButtonClicked(messageId) {
+ this.props.onMessageButtonClick(messageId);
+ this.hideMessage(messageId);
+ }
+
+ onCloseMessage(messageId) {
+ this.hideMessage(messageId);
}
setHideTimeout(messageData) {
@@ -69,7 +74,7 @@ class SnackBar extends React.Component {
element
.hide()
.then(() => delete this.messages[id])
- .then(() => this.props.deleteMessage(id));
+ .then(() => this.props.onDeleteMessage(id));
}
addMessages(messages) {
@@ -95,6 +100,7 @@ class SnackBar extends React.Component {
key={messageData.id}
message={messageData}
onClickMessageButton={this.onButtonClicked}
+ onCloseMessage={this.onCloseMessage}
/>
),
};
@@ -121,8 +127,8 @@ class SnackBar extends React.Component {
SnackBar.propTypes = {
errors: PropTypes.object,
- onClickMessageButton: PropTypes.func,
- deleteMessage: PropTypes.func,
+ onDeleteMessage: PropTypes.func,
+ onMessageButtonClick: PropTypes.func,
};
const mapStateToProps = state => ({
@@ -130,7 +136,8 @@ const mapStateToProps = state => ({
});
const mapDispatchToProps = dispatch => (bindActionCreators({
- deleteMessage,
+ onDeleteMessage: deleteMessage,
+ onMessageButtonClick: messageButtonClick,
}, dispatch));
export default connect(mapStateToProps, mapDispatchToProps)(SnackBar);
diff --git a/packages/xod-client/src/messages/reducer.js b/packages/xod-client/src/messages/reducer.js
index 9979000b..cf8808bb 100644
--- a/packages/xod-client/src/messages/reducer.js
+++ b/packages/xod-client/src/messages/reducer.js
@@ -5,7 +5,7 @@ import { getNewId } from './selectors';
export default (messages = {}, action) => {
switch (action.type) {
case MESSAGE_ADD: {
- const newId = getNewId(messages);
+ const newId = (action.meta.id) ? action.meta.id : getNewId(messages);
return R.assoc(
newId,
{
diff --git a/packages/xod-client/src/project/actions.js b/packages/xod-client/src/project/actions.js
index 9b3fdbce..2f20ac78 100644
--- a/packages/xod-client/src/project/actions.js
+++ b/packages/xod-client/src/project/actions.js
@@ -6,7 +6,7 @@ import { foldMaybe, rejectWithCode } from 'xod-func-tools';
import { addConfirmation, addError } from '../messages/actions';
import { NODETYPE_ERROR_TYPES } from '../editor/constants';
-import { PROJECT_BROWSER_ERRORS, NODETYPE_ERRORS } from '../messages/constants';
+import { PROJECT_BROWSER_ERRORS } from '../projectBrowser/messages';
import * as ActionType from './actionTypes';
import { isPatchPathTaken } from './utils';
import { getCurrentPatchPath } from '../editor/selectors';
@@ -14,9 +14,10 @@ import { getGrant } from '../user/selectors';
import { fetchGrant } from '../user/actions';
import { LOG_IN_TO_CONTINUE } from '../user/messages';
import { AUTHORIZATION_NEEDED } from '../user/errorCodes';
-import { SUCCESSFULLY_PUBLISHED } from './messages';
+import { SUCCESSFULLY_PUBLISHED, NODETYPE_ERRORS } from './messages';
import { getProject } from './selectors';
import { getPmSwaggerUrl } from '../utils/urls';
+import composeMessage from '../messages/composeMessage';
//
// Project
@@ -114,7 +115,7 @@ export const publishProject = () => (dispatch, getState) => {
})
.catch((err) => {
dispatch({ type: ActionType.PROJECT_PUBLISH_FAIL });
- dispatch(addError(err.message));
+ dispatch(addError(composeMessage(err.message)));
});
};
diff --git a/packages/xod-client/src/project/messages.js b/packages/xod-client/src/project/messages.js
index 916b9f11..e8b3fd21 100644
--- a/packages/xod-client/src/project/messages.js
+++ b/packages/xod-client/src/project/messages.js
@@ -1,2 +1,32 @@
-// eslint-disable-next-line import/prefer-default-export
-export const SUCCESSFULLY_PUBLISHED = 'Successfully published';
+import composeMessage from '../messages/composeMessage';
+import {
+ LINK_ERRORS as LE,
+ NODETYPE_ERROR_TYPES as NTE,
+} from '../editor/constants';
+
+export const SUCCESSFULLY_PUBLISHED = composeMessage('Library published');
+
+export const LINK_ERRORS = {
+ [LE.SAME_DIRECTION]: composeMessage('Canʼt create link between pins of the same direction!'),
+ [LE.SAME_NODE]: composeMessage('Canʼt create link between pins of the same node!'),
+ [LE.ONE_LINK_FOR_INPUT_PIN]: composeMessage('Input pin can have only one link!'),
+ [LE.UNKNOWN_ERROR]: composeMessage('Canʼt create link', 'Unknown error!'),
+ [LE.INCOMPATIBLE_TYPES]: composeMessage('Incompatible pin types!'),
+};
+
+export const NODETYPE_ERRORS = {
+ [NTE.CANT_DELETE_USED_PATCHNODE]: (
+ composeMessage(
+ 'Canʼt delete Patch',
+ 'Current Patch Node is used somewhere. You should remove it first!'
+ )
+ ),
+ [NTE.CANT_DELETE_USED_PIN_OF_PATCHNODE]: composeMessage(
+ 'Canʼt delete Pin',
+ [
+ 'Current IO Node is represents a Pin of Patch Node.',
+ 'And it is used somewhere.',
+ 'You should remove a linkage first!',
+ ].join(' ')
+ ),
+};
diff --git a/packages/xod-client/src/projectBrowser/messages.js b/packages/xod-client/src/projectBrowser/messages.js
new file mode 100644
index 00000000..1d4fe5c8
--- /dev/null
+++ b/packages/xod-client/src/projectBrowser/messages.js
@@ -0,0 +1,8 @@
+import composeMessage from '../messages/composeMessage';
+
+// eslint-disable-next-line import/prefer-default-export
+export const PROJECT_BROWSER_ERRORS = {
+ PATCH_NAME_TAKEN: composeMessage('This patch name is already taken!'),
+ INVALID_PATCH_NAME: composeMessage('Invalid patch name'),
+ INVALID_PROJECT_NAME: composeMessage('Invalid project name'),
+};
diff --git a/packages/xod-client/src/user/messages.js b/packages/xod-client/src/user/messages.js
index 1503dc5b..ecbc0a63 100644
--- a/packages/xod-client/src/user/messages.js
+++ b/packages/xod-client/src/user/messages.js
@@ -1,3 +1,5 @@
-export const INCORRECT_CREDENTIALS = 'Incorrect username or password';
-export const SERVICE_UNAVAILABLE = 'Service unavailable';
-export const LOG_IN_TO_CONTINUE = 'Please log in to continue';
+import composeMessage from '../messages/composeMessage';
+
+export const INCORRECT_CREDENTIALS = composeMessage('Incorrect username or password');
+export const SERVICE_UNAVAILABLE = composeMessage('Service unavailable');
+export const LOG_IN_TO_CONTINUE = composeMessage('Please log in to continue');
diff --git a/packages/xod-client/stories/SnackBarMessage.jsx b/packages/xod-client/stories/SnackBarMessage.jsx
index a1fcdbee..ecc87fff 100644
--- a/packages/xod-client/stories/SnackBarMessage.jsx
+++ b/packages/xod-client/stories/SnackBarMessage.jsx
@@ -6,45 +6,66 @@ import '../src/core/styles/main.scss';
import SnackBarMessage from '../src/messages/components/SnackBarMessage';
import { MESSAGE_TYPE } from '../src/messages/constants';
-const err = {
- id: 1,
+const errMsg = {
+ id: 0,
payload: {
- message: 'Something bad just happened',
- buttons: [],
+ title: 'Error',
+ note: 'Something bad just happened. And we just want to notify you. Without any actions required.',
+ },
+ timestamp: 1234567890,
+ type: MESSAGE_TYPE.ERROR,
+};
+const errMsgWithButton = {
+ id: 'errorWithButton',
+ persistent: true,
+ payload: {
+ title: 'Error occured. What shall we do now?',
+ note: 'Just showed error message, choose what to do now to fix it up?',
+ button: 'Retry',
},
timestamp: 1234567890,
type: MESSAGE_TYPE.ERROR,
};
const confirmationMsg = {
- id: 2,
+ id: 1,
payload: {
- message: 'Please confirm something',
- buttons: [],
+ title: 'Confirmation',
+ note: 'Message that confirms something. For example, it tells User that Project was successfully saved.',
+ },
+ timestamp: 1234567890,
+ type: MESSAGE_TYPE.CONFIRMATION,
+};
+
+const confirmationMsgWithButton = {
+ id: 'confirmationWithButton',
+ persistent: true,
+ payload: {
+ title: 'Confirmation with buttons',
+ note: 'We have a new update of XOD. What do you want to do with it?',
+ button: 'Download & Install',
},
timestamp: 1234567890,
type: MESSAGE_TYPE.CONFIRMATION,
};
const notificationMsg = {
- id: 3,
+ id: 2,
payload: {
- message: 'Something happened. Just wanted you to know.',
- buttons: [],
+ title: 'Some notification',
+ note: 'Something happened. Just wanted you to know.',
},
timestamp: 1234567890,
type: MESSAGE_TYPE.NOTIFICATION,
};
-const notificationWithBtns = {
- id: 4,
+const notificationWithButton = {
+ id: 'notificationWithButton',
+ persistent: true,
payload: {
- message: 'Something happened. What should we do with it?',
- buttons: [
- { id: 'abort', text: 'Abort' },
- { id: 'retry', text: 'Retry' },
- { id: 'ignore', text: 'Ignore' },
- ],
+ title: 'Notification with buttons',
+ note: 'Something happened. What should we do with it?',
+ button: 'Learn more',
},
timestamp: 1234567890,
type: MESSAGE_TYPE.CONFIRMATION,
@@ -58,7 +79,7 @@ storiesOf('SnackBarMessage', module)
))
.add('error', () => (
))
.add('confirmation', () => (
@@ -73,23 +94,34 @@ storiesOf('SnackBarMessage', module)
))
.add('notification with buttons', () => (
))
.add('multiple messages', () => (
diff --git a/packages/xod-client/test/project.spec.js b/packages/xod-client/test/project.spec.js
index 5812807e..d65d6e4f 100644
--- a/packages/xod-client/test/project.spec.js
+++ b/packages/xod-client/test/project.spec.js
@@ -13,7 +13,7 @@ import generateReducers from '../src/core/reducer';
import { getProject } from '../src/project/selectors';
import { NODETYPE_ERROR_TYPES } from '../src/editor/constants';
-import { NODETYPE_ERRORS } from '../src/messages/constants';
+import { NODETYPE_ERRORS } from '../src/project/messages';
import { addError } from '../src/messages/actions';
import { switchPatch } from '../src/editor/actions';