diff --git a/package.json b/package.json
index 643db793..1a18b689 100644
--- a/package.json
+++ b/package.json
@@ -124,12 +124,14 @@
"react-toolbox": "^1.2.1",
"react-waypoint": "^3.1.3",
"redux": "^3.3.1",
+ "redux-optimist": "^0.0.2",
"redux-notifications": "^2.1.1",
"redux-thunk": "^1.0.3",
"selection-position": "^1.0.0",
"semaphore": "^1.0.5",
"slate": "^0.14.14",
"slate-drop-or-paste-images": "^0.2.0",
+ "uuid": "^2.0.3",
"whatwg-fetch": "^1.0.0"
}
}
diff --git a/src/actions/editorialWorkflow.js b/src/actions/editorialWorkflow.js
index 9b1cb70b..acda56f8 100644
--- a/src/actions/editorialWorkflow.js
+++ b/src/actions/editorialWorkflow.js
@@ -1,3 +1,5 @@
+import uuid from 'uuid';
+import { BEGIN, COMMIT, REVERT } from 'redux-optimist';
import { currentBackend } from '../backends/backend';
import { getMedia } from '../reducers';
import { EDITORIAL_WORKFLOW } from '../constants/publishModes';
@@ -16,9 +18,12 @@ export const UNPUBLISHED_ENTRY_PERSIST_SUCCESS = 'UNPUBLISHED_ENTRY_PERSIST_SUCC
export const UNPUBLISHED_ENTRY_STATUS_CHANGE_REQUEST = 'UNPUBLISHED_ENTRY_STATUS_CHANGE_REQUEST';
export const UNPUBLISHED_ENTRY_STATUS_CHANGE_SUCCESS = 'UNPUBLISHED_ENTRY_STATUS_CHANGE_SUCCESS';
+export const UNPUBLISHED_ENTRY_STATUS_CHANGE_FAILURE = 'UNPUBLISHED_ENTRY_STATUS_CHANGE_FAILURE';
+
export const UNPUBLISHED_ENTRY_PUBLISH_REQUEST = 'UNPUBLISHED_ENTRY_PUBLISH_REQUEST';
export const UNPUBLISHED_ENTRY_PUBLISH_SUCCESS = 'UNPUBLISHED_ENTRY_PUBLISH_SUCCESS';
+export const UNPUBLISHED_ENTRY_PUBLISH_FAILURE = 'UNPUBLISHED_ENTRY_PUBLISH_FAILURE';
/*
* Simple Action Creators (Internal)
@@ -27,20 +32,20 @@ export const UNPUBLISHED_ENTRY_PUBLISH_SUCCESS = 'UNPUBLISHED_ENTRY_PUBLISH_SUCC
function unpublishedEntryLoading(status, slug) {
return {
type: UNPUBLISHED_ENTRY_REQUEST,
- payload: { status, slug }
+ payload: { status, slug },
};
}
function unpublishedEntryLoaded(status, entry) {
return {
type: UNPUBLISHED_ENTRY_SUCCESS,
- payload: { status, entry }
+ payload: { status, entry },
};
}
function unpublishedEntriesLoading() {
return {
- type: UNPUBLISHED_ENTRIES_REQUEST
+ type: UNPUBLISHED_ENTRIES_REQUEST,
};
}
@@ -48,9 +53,9 @@ function unpublishedEntriesLoaded(entries, pagination) {
return {
type: UNPUBLISHED_ENTRIES_SUCCESS,
payload: {
- entries: entries,
- pages: pagination
- }
+ entries,
+ pages: pagination,
+ },
};
}
@@ -66,49 +71,69 @@ function unpublishedEntriesFailed(error) {
function unpublishedEntryPersisting(entry) {
return {
type: UNPUBLISHED_ENTRY_PERSIST_REQUEST,
- payload: { entry }
+ payload: { entry },
};
}
function unpublishedEntryPersisted(entry) {
return {
type: UNPUBLISHED_ENTRY_PERSIST_SUCCESS,
- payload: { entry }
+ payload: { entry },
};
}
function unpublishedEntryPersistedFail(error) {
return {
type: UNPUBLISHED_ENTRY_PERSIST_SUCCESS,
- payload: { error }
+ payload: { error },
};
}
-function unpublishedEntryStatusChangeRequest(collection, slug, oldStatus, newStatus) {
+function unpublishedEntryStatusChangeRequest(collection, slug, oldStatus, newStatus, transactionID) {
return {
type: UNPUBLISHED_ENTRY_STATUS_CHANGE_REQUEST,
- payload: { collection, slug, oldStatus, newStatus }
+ payload: { collection, slug, oldStatus, newStatus },
+ optimist: { type: BEGIN, id: transactionID },
};
}
-function unpublishedEntryStatusChangePersisted(collection, slug, oldStatus, newStatus) {
+function unpublishedEntryStatusChangePersisted(collection, slug, oldStatus, newStatus, transactionID) {
return {
type: UNPUBLISHED_ENTRY_STATUS_CHANGE_SUCCESS,
- payload: { collection, slug, oldStatus, newStatus }
+ payload: { collection, slug, oldStatus, newStatus },
+ optimist: { type: COMMIT, id: transactionID },
};
}
-function unpublishedEntryPublishRequest(collection, slug, status) {
+function unpublishedEntryStatusChangeError(collection, slug, transactionID) {
+ return {
+ type: UNPUBLISHED_ENTRY_STATUS_CHANGE_FAILURE,
+ payload: { collection, slug },
+ optimist: { type: REVERT, id: transactionID },
+ };
+}
+
+function unpublishedEntryPublishRequest(collection, slug, status, transactionID) {
return {
type: UNPUBLISHED_ENTRY_PUBLISH_REQUEST,
- payload: { collection, slug, status }
+ payload: { collection, slug, status },
+ optimist: { type: BEGIN, id: transactionID },
};
}
-function unpublishedEntryPublished(collection, slug, status) {
+function unpublishedEntryPublished(collection, slug, status, transactionID) {
return {
type: UNPUBLISHED_ENTRY_PUBLISH_SUCCESS,
- payload: { collection, slug, status }
+ payload: { collection, slug, status },
+ optimist: { type: COMMIT, id: transactionID },
+ };
+}
+
+function unpublishedEntryPublishError(collection, slug, transactionID) {
+ return {
+ type: UNPUBLISHED_ENTRY_PUBLISH_FAILURE,
+ payload: { collection, slug },
+ optimist: { type: REVERT, id: transactionID },
};
}
@@ -122,7 +147,7 @@ export function loadUnpublishedEntry(collection, status, slug) {
const backend = currentBackend(state.config);
dispatch(unpublishedEntryLoading(status, slug));
backend.unpublishedEntry(collection, slug)
- .then((entry) => dispatch(unpublishedEntryLoaded(status, entry)));
+ .then(entry => dispatch(unpublishedEntryLoaded(status, entry)));
};
}
@@ -133,8 +158,8 @@ export function loadUnpublishedEntries() {
const backend = currentBackend(state.config);
dispatch(unpublishedEntriesLoading());
backend.unpublishedEntries().then(
- (response) => dispatch(unpublishedEntriesLoaded(response.entries, response.pagination)),
- (error) => dispatch(unpublishedEntriesFailed(error))
+ response => dispatch(unpublishedEntriesLoaded(response.entries, response.pagination)),
+ error => dispatch(unpublishedEntriesFailed(error))
);
};
}
@@ -149,7 +174,7 @@ export function persistUnpublishedEntry(collection, entry) {
() => {
dispatch(unpublishedEntryPersisted(entry));
},
- (error) => dispatch(unpublishedEntryPersistedFail(error))
+ error => dispatch(unpublishedEntryPersistedFail(error))
);
};
}
@@ -158,10 +183,14 @@ export function updateUnpublishedEntryStatus(collection, slug, oldStatus, newSta
return (dispatch, getState) => {
const state = getState();
const backend = currentBackend(state.config);
- dispatch(unpublishedEntryStatusChangeRequest(collection, slug, oldStatus, newStatus));
+ const transactionID = uuid.v4();
+ dispatch(unpublishedEntryStatusChangeRequest(collection, slug, oldStatus, newStatus, transactionID));
backend.updateUnpublishedEntryStatus(collection, slug, newStatus)
.then(() => {
- dispatch(unpublishedEntryStatusChangePersisted(collection, slug, oldStatus, newStatus));
+ dispatch(unpublishedEntryStatusChangePersisted(collection, slug, oldStatus, newStatus, transactionID));
+ })
+ .catch(() => {
+ dispatch(unpublishedEntryStatusChangeError(collection, slug, transactionID));
});
};
}
@@ -170,10 +199,14 @@ export function publishUnpublishedEntry(collection, slug, status) {
return (dispatch, getState) => {
const state = getState();
const backend = currentBackend(state.config);
+ const transactionID = uuid.v4();
dispatch(unpublishedEntryPublishRequest(collection, slug, status));
- backend.publishUnpublishedEntry(collection, slug, status)
+ backend.publishUnpublishedEntry(collection, slug, status, transactionID)
.then(() => {
- dispatch(unpublishedEntryPublished(collection, slug, status));
+ dispatch(unpublishedEntryPublished(collection, slug, status, transactionID));
+ })
+ .catch(() => {
+ dispatch(unpublishedEntryPublishError(collection, slug, transactionID));
});
};
}
diff --git a/src/backends/github/API.js b/src/backends/github/API.js
index 71818899..645bd4d3 100644
--- a/src/backends/github/API.js
+++ b/src/backends/github/API.js
@@ -165,7 +165,10 @@ export default class API {
}
persistFiles(entry, mediaFiles, options) {
- let filename, part, parts, subtree;
+ let filename,
+ part,
+ parts,
+ subtree;
const fileTree = {};
const uploadPromises = [];
@@ -269,7 +272,7 @@ export default class API {
}
updateUnpublishedEntryStatus(collection, slug, status) {
- const contentKey = collection ? `${ collection }-${ slug }` : slug;
+ const contentKey = slug;
return this.retrieveMetadata(contentKey)
.then((metadata) => {
return {
@@ -281,7 +284,7 @@ export default class API {
}
publishUnpublishedEntry(collection, slug, status) {
- const contentKey = collection ? `${ collection }-${ slug }` : slug;
+ const contentKey = slug;
return this.retrieveMetadata(contentKey)
.then((metadata) => {
const headSha = metadata.pr && metadata.pr.head;
@@ -376,7 +379,9 @@ export default class API {
updateTree(sha, path, fileTree) {
return this.getTree(sha)
.then((tree) => {
- let obj, filename, fileOrDir;
+ let obj,
+ filename,
+ fileOrDir;
const updates = [];
const added = {};
diff --git a/src/backends/github/implementation.js b/src/backends/github/implementation.js
index 5168edd1..79ab830f 100644
--- a/src/backends/github/implementation.js
+++ b/src/backends/github/implementation.js
@@ -83,7 +83,7 @@ export default class GitHub {
sem.leave();
} else {
const entryPath = data.metaData.objects.entry;
- const entry = createEntry('draft', entryPath.split('/').pop().replace(/\.[^\.]+$/, ''), entryPath, { raw: data.file });
+ const entry = createEntry('draft', contentKey, entryPath, { raw: data.file });
entry.metaData = data.metaData;
resolve(entry);
sem.leave();
diff --git a/src/components/FindBar/FindBar.js b/src/components/FindBar/FindBar.js
index fe670643..a9e12896 100644
--- a/src/components/FindBar/FindBar.js
+++ b/src/components/FindBar/FindBar.js
@@ -12,7 +12,7 @@ class FindBar extends Component {
commands: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
- pattern: PropTypes.string.isRequired
+ pattern: PropTypes.string.isRequired,
})).isRequired,
defaultCommands: PropTypes.arrayOf(PropTypes.string),
runCommand: PropTypes.func.isRequired,
@@ -23,9 +23,9 @@ class FindBar extends Component {
this._compiledCommands = [];
this._searchCommand = {
search: true,
- regexp: `(?:${SEARCH})?(.*)`,
+ regexp: `(?:${ SEARCH })?(.*)`,
param: { name: 'searchTerm', display: '' },
- token: SEARCH
+ token: SEARCH,
};
this.state = {
value: '',
@@ -56,7 +56,7 @@ class FindBar extends Component {
}
// Generates a regexp and splits a token and param details for a command
- compileCommand = command => {
+ compileCommand = (command) => {
let regexp = '';
let param = null;
@@ -75,7 +75,7 @@ class FindBar extends Component {
return Object.assign({}, command, {
regexp,
token,
- param
+ param,
});
};
@@ -84,15 +84,15 @@ class FindBar extends Component {
matchCommand = () => {
const string = this.state.activeScope ? this.state.activeScope + this.state.value : this.state.value;
let match;
- let command = this._compiledCommands.find(command => {
- match = string.match(RegExp(`^${command.regexp}`, 'i'));
+ let command = this._compiledCommands.find((command) => {
+ match = string.match(RegExp(`^${ command.regexp }`, 'i'));
return match;
});
// If no command was found, trigger a search command
if (!command) {
command = this._searchCommand;
- match = string.match(RegExp(`^${this._searchCommand.regexp}`, 'i'));
+ match = string.match(RegExp(`^${ this._searchCommand.regexp }`, 'i'));
}
const paramName = command && command.param ? command.param.name : null;
@@ -101,7 +101,7 @@ class FindBar extends Component {
if (command.search) {
this.setState({
activeScope: SEARCH,
- placeholder: ''
+ placeholder: '',
});
enteredParamValue && this.props.runCommand(SEARCH, { searchTerm: enteredParamValue });
@@ -112,7 +112,7 @@ class FindBar extends Component {
this.setState({
value: '',
activeScope: command.token,
- placeholder: command.param.display
+ placeholder: command.param.display,
});
} else {
// Match
@@ -121,7 +121,7 @@ class FindBar extends Component {
this.setState({
value: '',
placeholder: PLACEHOLDER,
- activeScope: null
+ activeScope: null,
}, () => {
this._input.blur();
});
@@ -137,7 +137,7 @@ class FindBar extends Component {
if (this.state.value.length === 0 && this.state.activeScope) {
this.setState({
activeScope: null,
- placeholder: PLACEHOLDER
+ placeholder: PLACEHOLDER,
});
}
};
@@ -160,7 +160,7 @@ class FindBar extends Component {
const results = fuzzy.filter(value, commands, {
pre: '',
post: '',
- extract: el => el.token
+ extract: el => el.token,
});
const returnResults = results.slice(0, 4).map(result => (
@@ -171,8 +171,9 @@ class FindBar extends Component {
return returnResults;
}
- handleKeyDown = event => {
- let highlightedIndex, index;
+ handleKeyDown = (event) => {
+ let highlightedIndex,
+ index;
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
@@ -202,7 +203,7 @@ class FindBar extends Component {
const command = this.getSuggestions()[this.state.highlightedIndex];
const newState = {
isOpen: false,
- highlightedIndex: 0
+ highlightedIndex: 0,
};
if (command && !command.search) {
newState.value = command.token;
@@ -223,24 +224,24 @@ class FindBar extends Component {
highlightedIndex: 0,
isOpen: false,
activeScope: null,
- placeholder: PLACEHOLDER
+ placeholder: PLACEHOLDER,
});
break;
case 'Backspace':
this.setState({
highlightedIndex: 0,
- isOpen: true
+ isOpen: true,
}, this.maybeRemoveActiveScope);
break;
default:
this.setState({
highlightedIndex: 0,
- isOpen: true
+ isOpen: true,
});
}
};
- handleChange = event => {
+ handleChange = (event) => {
this.setState({
value: event.target.value,
});
@@ -250,7 +251,7 @@ class FindBar extends Component {
if (this._ignoreBlur) return;
this.setState({
isOpen: false,
- highlightedIndex: 0
+ highlightedIndex: 0,
});
};
@@ -261,17 +262,17 @@ class FindBar extends Component {
handleInputClick = () => {
if (this.state.isOpen === false)
- this.setState({ isOpen: true });
+ { this.setState({ isOpen: true }); }
};
- highlightCommandFromMouse = index => {
+ highlightCommandFromMouse = (index) => {
this.setState({ highlightedIndex: index });
};
- selectCommandFromMouse = command => {
+ selectCommandFromMouse = (command) => {
const newState = {
isOpen: false,
- highlightedIndex: 0
+ highlightedIndex: 0,
};
if (command && !command.search) {
newState.value = command.token;
@@ -283,7 +284,7 @@ class FindBar extends Component {
});
};
- setIgnoreBlur = ignore => {
+ setIgnoreBlur = (ignore) => {
this._ignoreBlur = ignore;
};
@@ -292,14 +293,14 @@ class FindBar extends Component {
let children;
if (!command.search) {
children = (
-
+
);
} else {
children = (
- {this.state.value.length === 0 ?
- Search... :
- Search for:
+ {this.state.value.length === 0 ?
+ Search... :
+ Search for:
}
{this.state.value}
@@ -331,7 +332,7 @@ class FindBar extends Component {
renderActiveScope() {
if (this.state.activeScope === SEARCH) {
- return
;
+ return
;
} else {
return {this.state.activeScope}
;
}
@@ -346,7 +347,7 @@ class FindBar extends Component {
{scope}
this._input = c}
+ ref={c => this._input = c}
onFocus={this.handleInputFocus}
onBlur={this.handleInputBlur}
onChange={this.handleChange}
diff --git a/src/components/MarkupItReactRenderer/index.js b/src/components/MarkupItReactRenderer/index.js
index 24c64d4c..f5ede88b 100644
--- a/src/components/MarkupItReactRenderer/index.js
+++ b/src/components/MarkupItReactRenderer/index.js
@@ -10,7 +10,7 @@ const defaultSchema = {
[BLOCKS.PARAGRAPH]: 'p',
[BLOCKS.FOOTNOTE]: 'footnote',
[BLOCKS.HTML]: ({ token }) => {
- return ;
+ return ;
},
[BLOCKS.HR]: 'hr',
[BLOCKS.HEADING_1]: 'h1',
@@ -35,7 +35,7 @@ const defaultSchema = {
[ENTITIES.LINK]: 'a',
[ENTITIES.IMAGE]: 'img',
[ENTITIES.FOOTNOTE_REF]: 'sup',
- [ENTITIES.HARD_BREAK]: 'br'
+ [ENTITIES.HARD_BREAK]: 'br',
};
const notAllowedAttributes = ['loose'];
@@ -50,7 +50,7 @@ function renderToken(schema, token, index = 0, key = '0') {
const text = token.get('text');
const tokens = token.get('tokens');
const nodeType = schema[type];
- key = `${key}.${index}`;
+ key = `${ key }.${ index }`;
// Only render if type is registered as renderer
if (typeof nodeType !== 'undefined') {
@@ -101,6 +101,6 @@ MarkupItReactRenderer.propTypes = {
syntax: PropTypes.instanceOf(Syntax).isRequired,
schema: PropTypes.objectOf(PropTypes.oneOfType([
PropTypes.string,
- PropTypes.func
- ]))
+ PropTypes.func,
+ ])),
};
diff --git a/src/components/PreviewPane/PreviewPane.js b/src/components/PreviewPane/PreviewPane.js
index 8134d0a8..5ee0efd2 100644
--- a/src/components/PreviewPane/PreviewPane.js
+++ b/src/components/PreviewPane/PreviewPane.js
@@ -13,9 +13,9 @@ export default class PreviewPane extends React.Component {
this.renderPreview();
}
- widgetFor = name => {
+ widgetFor = (name) => {
const { collection, entry, getMedia } = this.props;
- const field = collection.get('fields').find((field) => field.get('name') === name);
+ const field = collection.get('fields').find(field => field.get('name') === name);
const widget = resolveWidget(field.get('widget'));
return React.createElement(widget.preview, {
key: field.get('name'),
@@ -29,7 +29,7 @@ export default class PreviewPane extends React.Component {
const component = registry.getPreviewTemplate(this.props.collection.get('name')) || Preview;
const previewProps = {
...this.props,
- widgetFor: this.widgetFor
+ widgetFor: this.widgetFor,
};
// We need to use this API in order to pass context to the iframe
ReactDOM.unstable_renderSubtreeIntoContainer(
@@ -40,7 +40,7 @@ export default class PreviewPane extends React.Component {
, this.previewEl);
}
- handleIframeRef = ref => {
+ handleIframeRef = (ref) => {
if (ref) {
registry.getPreviewStyles().forEach((style) => {
const linkEl = document.createElement('link');
@@ -61,7 +61,7 @@ export default class PreviewPane extends React.Component {
return null;
}
- return ;
+ return ;
}
}
diff --git a/src/components/ScrollSync/ScrollSync.js b/src/components/ScrollSync/ScrollSync.js
index 35ce2fc4..5d921488 100644
--- a/src/components/ScrollSync/ScrollSync.js
+++ b/src/components/ScrollSync/ScrollSync.js
@@ -21,44 +21,44 @@ export default class ScrollSync extends Component {
};
}
- registerPane = node => {
+ registerPane = (node) => {
if (!this.findPane(node)) {
this.addEvents(node);
this.panes.push(node);
}
};
- unregisterPane = node => {
+ unregisterPane = (node) => {
if (this.findPane(node)) {
this.removeEvents(node);
this.panes = without(this.panes, node);
}
};
- addEvents = node => {
+ addEvents = (node) => {
node.onscroll = this.handlePaneScroll.bind(this, node);
// node.addEventListener('scroll', this.handlePaneScroll, false)
};
- removeEvents = node => {
+ removeEvents = (node) => {
node.onscroll = null;
// node.removeEventListener('scroll', this.handlePaneScroll, false)
};
- findPane = node => {
+ findPane = (node) => {
return this.panes.find(p => p === node);
};
- handlePaneScroll = node => {
+ handlePaneScroll = (node) => {
// const node = evt.target
window.requestAnimationFrame(() => {
this.syncScrollPositions(node);
});
};
- syncScrollPositions = scrolledPane => {
+ syncScrollPositions = (scrolledPane) => {
const { scrollTop, scrollHeight, clientHeight } = scrolledPane;
- this.panes.forEach(pane => {
+ this.panes.forEach((pane) => {
/* For all panes beside the currently scrolling one */
if (scrolledPane !== pane) {
/* Remove event listeners from the node that we'll manipulate */
diff --git a/src/components/ScrollSync/ScrollSyncPane.js b/src/components/ScrollSync/ScrollSyncPane.js
index 840edf59..6fcf7a67 100644
--- a/src/components/ScrollSync/ScrollSyncPane.js
+++ b/src/components/ScrollSync/ScrollSyncPane.js
@@ -5,7 +5,7 @@ export default class ScrollSyncPane extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
- attachTo: PropTypes.any
+ attachTo: PropTypes.any,
};
static contextTypes = {
diff --git a/src/components/UI/loader/Loader.js b/src/components/UI/loader/Loader.js
index 0c698008..c3c91ffc 100644
--- a/src/components/UI/loader/Loader.js
+++ b/src/components/UI/loader/Loader.js
@@ -19,7 +19,6 @@ export default class Loader extends React.Component {
const { children } = this.props;
this.interval = setInterval(() => {
-
const nextItem = (this.state.currentItem === children.length - 1) ? 0 : this.state.currentItem + 1;
this.setState({ currentItem: nextItem });
}, 5000);
@@ -34,7 +33,7 @@ export default class Loader extends React.Component {
return {children}
;
} else if (Array.isArray(children)) {
this.setAnimation();
- return
+ return (
{children[currentItem]}
-
;
+
);
}
};
@@ -52,13 +51,12 @@ export default class Loader extends React.Component {
// Class names
let classNames = styles.loader;
if (active) {
- classNames += ` ${styles.active}`;
+ classNames += ` ${ styles.active }`;
}
if (className.length > 0) {
- classNames += ` ${className}`;
+ classNames += ` ${ className }`;
}
return {this.renderChild()}
;
-
}
}
diff --git a/src/components/UnpublishedListing.js b/src/components/UnpublishedListing.js
index e7df60d2..7717ea71 100644
--- a/src/components/UnpublishedListing.js
+++ b/src/components/UnpublishedListing.js
@@ -16,9 +16,6 @@ class UnpublishedListing extends React.Component {
};
requestPublish = (collection, slug, ownStatus) => {
- console.log('HERE');
- console.log(ownStatus);
- console.log(status.last());
if (ownStatus !== status.last()) return;
if (window.confirm('Are you sure you want to publish this entry?')) {
this.props.handlePublish(collection, slug, ownStatus);
diff --git a/src/components/Widgets/ImageControl.js b/src/components/Widgets/ImageControl.js
index a7fc358c..890adf90 100644
--- a/src/components/Widgets/ImageControl.js
+++ b/src/components/Widgets/ImageControl.js
@@ -5,25 +5,25 @@ import MediaProxy from '../../valueObjects/MediaProxy';
const MAX_DISPLAY_LENGTH = 50;
export default class ImageControl extends React.Component {
- handleFileInputRef = el => {
+ handleFileInputRef = (el) => {
this._fileInput = el;
};
- handleClick = e => {
+ handleClick = (e) => {
this._fileInput.click();
};
- handleDragEnter = e => {
+ handleDragEnter = (e) => {
e.stopPropagation();
e.preventDefault();
};
- handleDragOver = e => {
+ handleDragOver = (e) => {
e.stopPropagation();
e.preventDefault();
};
- handleChange = e => {
+ handleChange = (e) => {
e.stopPropagation();
e.preventDefault();
@@ -46,7 +46,6 @@ export default class ImageControl extends React.Component {
} else {
this.props.onChange(null);
}
-
};
renderImageName = () => {
@@ -56,7 +55,6 @@ export default class ImageControl extends React.Component {
} else {
return truncateMiddle(this.props.value, MAX_DISPLAY_LENGTH);
}
-
};
render() {
@@ -84,7 +82,7 @@ export default class ImageControl extends React.Component {
const styles = {
input: {
- display: 'none'
+ display: 'none',
},
imageUpload: {
backgroundColor: '#fff',
@@ -94,8 +92,8 @@ const styles = {
display: 'block',
border: '1px dashed #eee',
cursor: 'pointer',
- fontSize: '12px'
- }
+ fontSize: '12px',
+ },
};
ImageControl.propTypes = {
diff --git a/src/components/Widgets/MarkdownControl.js b/src/components/Widgets/MarkdownControl.js
index 94cfbb43..473402ce 100644
--- a/src/components/Widgets/MarkdownControl.js
+++ b/src/components/Widgets/MarkdownControl.js
@@ -33,7 +33,7 @@ class MarkdownControl extends React.Component {
const { editor, onChange, onAddMedia, getMedia, value } = this.props;
if (editor.get('useVisualMode')) {
return (
-
+
{null &&
}
+
{null &&
}
object.kind == 'block',
- decorate: renderDecorations
- }
+ match: object => object.kind == 'block',
+ decorate: renderDecorations,
+ },
],
marks: {
'highlight-comment': {
- opacity: '0.33'
+ opacity: '0.33',
},
'highlight-important': {
fontWeight: 'bold',
@@ -68,8 +68,8 @@ const SCHEMA = {
},
'highlight-punctuation': {
color: '#006',
- }
- }
+ },
+ },
};
export default class RawEditor extends React.Component {
@@ -86,19 +86,19 @@ export default class RawEditor extends React.Component {
const content = props.value ? Plain.deserialize(props.value) : Plain.deserialize('');
this.state = {
- state: content
+ state: content,
};
this.plugins = [
PluginDropImages({
applyTransform: (transform, file) => {
const mediaProxy = new MediaProxy(file.name, file);
- const state = Plain.deserialize(`\n\n\n\n`);
+ const state = Plain.deserialize(`\n\n\n\n`);
props.onAddMedia(mediaProxy);
return transform
.insertFragment(state.get('document'));
- }
- })
+ },
+ }),
];
}
@@ -108,7 +108,7 @@ export default class RawEditor extends React.Component {
* It also have an onDocumentChange, that get's dispatched only when the actual
* content changes
*/
- handleChange = state => {
+ handleChange = (state) => {
this.setState({ state });
};
diff --git a/src/components/Widgets/MarkdownControlElements/VisualEditor/BlockTypesMenu.js b/src/components/Widgets/MarkdownControlElements/VisualEditor/BlockTypesMenu.js
index 33d5e532..36ab5166 100644
--- a/src/components/Widgets/MarkdownControlElements/VisualEditor/BlockTypesMenu.js
+++ b/src/components/Widgets/MarkdownControlElements/VisualEditor/BlockTypesMenu.js
@@ -10,11 +10,11 @@ class BlockTypesMenu extends Component {
plugins: PropTypes.array.isRequired,
onClickBlock: PropTypes.func.isRequired,
onClickPlugin: PropTypes.func.isRequired,
- onClickImage: PropTypes.func.isRequired
+ onClickImage: PropTypes.func.isRequired,
};
state = {
- expanded: false
+ expanded: false,
};
componentWillUpdate() {
@@ -33,7 +33,7 @@ class BlockTypesMenu extends Component {
handlePluginClick = (e, plugin) => {
const data = {};
- plugin.fields.forEach(field => {
+ plugin.fields.forEach((field) => {
data[field.name] = window.prompt(field.label); // eslint-disable-line
});
this.props.onClickPlugin(plugin.id, data);
@@ -43,7 +43,7 @@ class BlockTypesMenu extends Component {
this._fileInput.click();
};
- handleFileUploadChange = e => {
+ handleFileUploadChange = (e) => {
e.stopPropagation();
e.preventDefault();
@@ -62,20 +62,19 @@ class BlockTypesMenu extends Component {
const mediaProxy = new MediaProxy(file.name, file);
this.props.onClickImage(mediaProxy);
}
-
};
renderBlockTypeButton = (type, icon) => {
const onClick = e => this.handleBlockTypeClick(e, type);
return (
-
+
);
};
- renderPluginButton = plugin => {
+ renderPluginButton = (plugin) => {
const onClick = e => this.handlePluginClick(e, plugin);
return (
-
+
);
};
@@ -86,13 +85,13 @@ class BlockTypesMenu extends Component {
{this.renderBlockTypeButton('hr', 'dot-3')}
{plugins.map(plugin => this.renderPluginButton(plugin))}
-
+
{
+ ref={(el) => {
this._fileInput = el;
}}
/>
@@ -106,7 +105,7 @@ class BlockTypesMenu extends Component {
render() {
return (
-
+
{this.renderMenu()}
);
diff --git a/src/components/Widgets/MarkdownControlElements/VisualEditor/StylesMenu.js b/src/components/Widgets/MarkdownControlElements/VisualEditor/StylesMenu.js
index 3ddf915d..5d141098 100644
--- a/src/components/Widgets/MarkdownControlElements/VisualEditor/StylesMenu.js
+++ b/src/components/Widgets/MarkdownControlElements/VisualEditor/StylesMenu.js
@@ -11,23 +11,23 @@ class StylesMenu extends Component {
inlines: PropTypes.object.isRequired,
onClickBlock: PropTypes.func.isRequired,
onClickMark: PropTypes.func.isRequired,
- onClickInline: PropTypes.func.isRequired
+ onClickInline: PropTypes.func.isRequired,
};
/**
* Used to set toolbar buttons to active state
*/
- hasMark = type => {
+ hasMark = (type) => {
const { marks } = this.props;
return marks.some(mark => mark.type == type);
};
- hasBlock = type => {
+ hasBlock = (type) => {
const { blocks } = this.props;
return blocks.some(node => node.type == type);
};
- hasLinks = type => {
+ hasLinks = (type) => {
const { inlines } = this.props;
return inlines.some(inline => inline.type == 'link');
};
@@ -42,7 +42,7 @@ class StylesMenu extends Component {
const onMouseDown = e => this.handleMarkClick(e, type);
return (
-
+
);
};
@@ -57,7 +57,7 @@ class StylesMenu extends Component {
const onMouseDown = e => this.handleInlineClick(e, 'link', isActive);
return (
-
+
);
};
@@ -75,14 +75,14 @@ class StylesMenu extends Component {
const onMouseDown = e => this.handleBlockClick(e, type);
return (
-
+
);
};
render() {
return (
-
+
{this.renderMarkButton('BOLD', 'bold')}
{this.renderMarkButton('ITALIC', 'italic')}
{this.renderMarkButton('CODE', 'code')}
diff --git a/src/components/Widgets/MarkdownControlElements/VisualEditor/index.js b/src/components/Widgets/MarkdownControlElements/VisualEditor/index.js
index 053c8847..943948e0 100644
--- a/src/components/Widgets/MarkdownControlElements/VisualEditor/index.js
+++ b/src/components/Widgets/MarkdownControlElements/VisualEditor/index.js
@@ -40,7 +40,7 @@ export default class VisualEditor extends React.Component {
rawJson = emptyParagraphBlock;
}
this.state = {
- state: Raw.deserialize(rawJson, { terse: true })
+ state: Raw.deserialize(rawJson, { terse: true }),
};
this.plugins = [
@@ -50,12 +50,12 @@ export default class VisualEditor extends React.Component {
props.onAddMedia(mediaProxy);
return transform
.insertBlock(mediaproxyBlock(mediaProxy));
- }
- })
+ },
+ }),
];
}
- getMedia = src => {
+ getMedia = (src) => {
return this.props.getMedia(src);
};
@@ -65,7 +65,7 @@ export default class VisualEditor extends React.Component {
* It also have an onDocumentChange, that get's dispatched only when the actual
* content changes
*/
- handleChange = state => {
+ handleChange = (state) => {
if (this.blockEdit) {
this.blockEdit = false;
} else {
@@ -82,7 +82,7 @@ export default class VisualEditor extends React.Component {
/**
* Toggle marks / blocks when button is clicked
*/
- handleMarkStyleClick = type => {
+ handleMarkStyleClick = (type) => {
let { state } = this.state;
state = state
@@ -100,7 +100,6 @@ export default class VisualEditor extends React.Component {
// Handle everything but list buttons.
if (type != 'unordered_list' && type != 'ordered_list') {
-
if (isList) {
transform = transform
.setBlock(isActive ? DEFAULT_NODE : type)
@@ -165,7 +164,7 @@ export default class VisualEditor extends React.Component {
.transform()
.wrapInline({
type: 'link',
- data: { href }
+ data: { href },
})
.collapseToEnd()
.apply();
@@ -174,14 +173,14 @@ export default class VisualEditor extends React.Component {
this.setState({ state });
};
- handleBlockTypeClick = type => {
+ handleBlockTypeClick = (type) => {
let { state } = this.state;
state = state
.transform()
.insertBlock({
- type: type,
- isVoid: true
+ type,
+ isVoid: true,
})
.apply();
@@ -194,9 +193,9 @@ export default class VisualEditor extends React.Component {
state = state
.transform()
.insertInline({
- type: type,
- data: data,
- isVoid: true
+ type,
+ data,
+ isVoid: true,
})
.collapseToEnd()
.insertBlock(DEFAULT_NODE)
@@ -206,7 +205,7 @@ export default class VisualEditor extends React.Component {
this.setState({ state });
};
- handleImageClick = mediaProxy => {
+ handleImageClick = (mediaProxy) => {
let { state } = this.state;
this.props.onAddMedia(mediaProxy);
@@ -229,12 +228,12 @@ export default class VisualEditor extends React.Component {
.splitBlock()
.setBlock(DEFAULT_NODE)
.apply({
- snapshot: false
+ snapshot: false,
});
this.setState({ state: normalized });
};
- handleKeyDown = evt => {
+ handleKeyDown = (evt) => {
if (evt.shiftKey && evt.key === 'Enter') {
this.blockEdit = true;
let { state } = this.state;
diff --git a/src/components/Widgets/MarkdownControlElements/VisualEditor/withPortalAtCursorPosition.js b/src/components/Widgets/MarkdownControlElements/VisualEditor/withPortalAtCursorPosition.js
index 4aab4ea6..4ff68cb0 100644
--- a/src/components/Widgets/MarkdownControlElements/VisualEditor/withPortalAtCursorPosition.js
+++ b/src/components/Widgets/MarkdownControlElements/VisualEditor/withPortalAtCursorPosition.js
@@ -6,13 +6,13 @@ export default function withPortalAtCursorPosition(WrappedComponent) {
return class extends React.Component {
static propTypes = {
- isOpen: React.PropTypes.bool.isRequired
- }
+ isOpen: React.PropTypes.bool.isRequired,
+ };
state = {
menu: null,
- cursorPosition: null
- }
+ cursorPosition: null,
+ };
componentDidMount() {
this.adjustPosition();
@@ -36,22 +36,22 @@ export default function withPortalAtCursorPosition(WrappedComponent) {
);
const centerY = cursorPosition.top + window.scrollY;
menu.style.opacity = 1;
- menu.style.top = `${centerY}px`;
- menu.style.left = `${centerX}px`;
- }
+ menu.style.top = `${ centerY }px`;
+ menu.style.left = `${ centerX }px`;
+ };
/**
* When the portal opens, cache the menu element.
*/
handleOpen = (portal) => {
this.setState({ menu: portal.firstChild });
- }
+ };
render() {
const { isOpen, ...rest } = this.props;
return (
-
+
);
}
diff --git a/src/components/Widgets/MarkdownControlElements/constants.js b/src/components/Widgets/MarkdownControlElements/constants.js
index c4609e2c..9f6469fb 100644
--- a/src/components/Widgets/MarkdownControlElements/constants.js
+++ b/src/components/Widgets/MarkdownControlElements/constants.js
@@ -6,11 +6,11 @@ export const emptyParagraphBlock = {
nodes: [{
kind: 'text',
ranges: [{
- text: ''
- }]
- }]
- }
- ]
+ text: '',
+ }],
+ }],
+ },
+ ],
};
export const mediaproxyBlock = mediaproxy => ({
@@ -22,7 +22,7 @@ export const mediaproxyBlock = mediaproxy => ({
isVoid: true,
data: {
alt: mediaproxy.name,
- src: mediaproxy.public_path
- }
- }]
+ src: mediaproxy.public_path,
+ },
+ }],
});
diff --git a/src/components/Widgets/MarkdownPreview.js b/src/components/Widgets/MarkdownPreview.js
index 3c84d884..9b1fb9c4 100644
--- a/src/components/Widgets/MarkdownPreview.js
+++ b/src/components/Widgets/MarkdownPreview.js
@@ -13,7 +13,7 @@ const MarkdownPreview = ({ value, getMedia }) => {
src={getMedia(token.getIn(['data', 'src']))}
alt={token.getIn(['data', 'alt'])}
/>
- )
+ ),
};
const { markdown } = getSyntaxes();
diff --git a/src/components/Widgets/richText.js b/src/components/Widgets/richText.js
index e3ce7bd5..39aadbee 100644
--- a/src/components/Widgets/richText.js
+++ b/src/components/Widgets/richText.js
@@ -24,13 +24,13 @@ function processEditorPlugins(plugins) {
// to determine whether we need to process again.
if (plugins === processedPlugins) return;
- plugins.forEach(plugin => {
+ plugins.forEach((plugin) => {
const basicRule = MarkupIt.Rule(plugin.id).regExp(plugin.pattern, (state, match) => (
{ data: plugin.fromBlock(match) }
));
const markdownRule = basicRule.toText((state, token) => (
- plugin.toBlock(token.getData().toObject()) + '\n\n'
+ `${ plugin.toBlock(token.getData().toObject()) }\n\n`
));
const htmlRule = basicRule.toText((state, token) => (
@@ -43,9 +43,9 @@ function processEditorPlugins(plugins) {
const className = isFocused ? 'plugin active' : 'plugin';
return (
-
+
- {plugin.fields.map(field => `${field.label}: “${node.data.get(field.name)}”`)}
+ {plugin.fields.map(field => `${ field.label }: “${ node.data.get(field.name) }”`)}
);
@@ -66,43 +66,43 @@ function processMediaProxyPlugins(getMedia) {
return;
}
- var imgData = Map({
+ const imgData = Map({
alt: match[1],
src: match[2],
- title: match[3]
+ title: match[3],
}).filter(Boolean);
return {
- data: imgData
+ data: imgData,
};
});
const mediaProxyMarkdownRule = mediaProxyRule.toText((state, token) => {
- var data = token.getData();
- var alt = data.get('alt', '');
- var src = data.get('src', '');
- var title = data.get('title', '');
+ const data = token.getData();
+ const alt = data.get('alt', '');
+ const src = data.get('src', '');
+ const title = data.get('title', '');
if (title) {
- return '';
+ return ``;
} else {
- return '';
+ return ``;
}
});
const mediaProxyHTMLRule = mediaProxyRule.toText((state, token) => {
- var data = token.getData();
- var alt = data.get('alt', '');
- var src = data.get('src', '');
- return `
})
`;
+ const data = token.getData();
+ const alt = data.get('alt', '');
+ const src = data.get('src', '');
+ return `

`;
});
- nodes['mediaproxy'] = (props) => {
+ nodes.mediaproxy = (props) => {
/* eslint react/prop-types: 0 */
const { node, state } = props;
const isFocused = state.selection.hasEdgeIn(node);
const className = isFocused ? 'active' : null;
const src = node.data.get('src');
return (
-
})
+
})
);
};
augmentedMarkdownSyntax = augmentedMarkdownSyntax.addInlineRules(mediaProxyMarkdownRule);
@@ -113,7 +113,7 @@ function getPlugins() {
return processedPlugins.map(plugin => ({
id: plugin.id,
icon: plugin.icon,
- fields: plugin.fields
+ fields: plugin.fields,
})).toArray();
}
diff --git a/src/components/stories/FindBar.js b/src/components/stories/FindBar.js
index b2933b96..9c375fed 100644
--- a/src/components/stories/FindBar.js
+++ b/src/components/stories/FindBar.js
@@ -27,16 +27,16 @@ const commands = [
const style = {
width: 800,
- margin: 20
+ margin: 20,
};
storiesOf('FindBar', module)
.add('Default View', () => (
));
diff --git a/src/components/stories/MarkupItReactRenderer.js b/src/components/stories/MarkupItReactRenderer.js
index 2ba1a3ee..07a1f28c 100644
--- a/src/components/stories/MarkupItReactRenderer.js
+++ b/src/components/stories/MarkupItReactRenderer.js
@@ -22,13 +22,13 @@ const htmlContent = `
storiesOf('MarkupItReactRenderer', module)
.add('Markdown', () => (
)).add('HTML', () => (
));
diff --git a/src/containers/App.css b/src/containers/App.css
index 2a9b5418..84287cf5 100644
--- a/src/containers/App.css
+++ b/src/containers/App.css
@@ -1,30 +1,280 @@
-@import '../components/UI/theme.css';
+@import "material-icons.css";
-.layout .navDrawer .drawerContent {
- padding-top: 54px;
- max-width: 240px;
+html {
+ box-sizing: border-box;
+ margin: 0;
+ font-family: Roboto, 'Helvetica Neue', HelveticaNeue, Helvetica, Arial, sans-serif;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
}
-.nav {
- display: block;
- padding: 1rem;
+*,
+*:before,
+*:after {
+ box-sizing: inherit;
+}
- & .heading {
+body {
+ margin: 0;
+ height: 100%;
+ background-color: #f2f5f4;
+ color: #7c8382;
+ font-family: 'Roboto', sans-serif;
+}
+
+:global #root,
+:global #root > * {
+ height: 100%;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p {
+ margin: 0;
+}
+
+h1 {
+ margin: 30px auto 25px;
+ padding-bottom: 15px;
+ border-bottom: 1px solid #3ab7a5;
+ color: #3ab7a5;
+ font-size: 25px;
+}
+
+:global {
+ & .rdt {
+ position: relative;
+ }
+
+ & .rdtPicker {
+ position: absolute;
+ z-index: 99999 !important;
+ display: none;
+ margin-top: 1px;
+ padding: 4px;
+ width: 250px;
+ border: 1px solid #f9f9f9;
+ background: #fff;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, .1);
+ }
+
+ & .rdtOpen .rdtPicker {
+ display: block;
+ }
+
+ & .rdtStatic .rdtPicker {
+ position: static;
+ box-shadow: none;
+ }
+
+ & .rdtPicker .rdtTimeToggle {
+ text-align: center;
+ }
+
+ & .rdtPicker table {
+ margin: 0;
+ width: 100%;
+ }
+
+ & .rdtPicker td,
+ & .rdtPicker th {
+ height: 28px;
+ text-align: center;
+ }
+
+ & .rdtPicker td {
+ cursor: pointer;
+ }
+
+ & .rdtPicker td.rdtDay:hover,
+ & .rdtPicker td.rdtHour:hover,
+ & .rdtPicker td.rdtMinute:hover,
+ & .rdtPicker td.rdtSecond:hover,
+ & .rdtPicker .rdtTimeToggle:hover {
+ background: #eeeeee;
+ cursor: pointer;
+ }
+
+ & .rdtPicker td.rdtOld,
+ & .rdtPicker td.rdtNew {
+ color: #999999;
+ }
+
+ & .rdtPicker td.rdtToday {
+ position: relative;
+ }
+
+ & .rdtPicker td.rdtToday:before {
+ position: absolute;
+ right: 4px;
+ bottom: 4px;
+ display: inline-block;
+ border-top-color: rgba(0, 0, 0, .2);
+ border-bottom: 7px solid #428bca;
+ border-left: 7px solid transparent;
+ content: '';
+ }
+
+ & .rdtPicker td.rdtActive,
+ & .rdtPicker td.rdtActive:hover {
+ background-color: #428bca;
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
+ }
+
+ & .rdtPicker td.rdtActive.rdtToday:before {
+ border-bottom-color: #fff;
+ }
+
+ & .rdtPicker td.rdtDisabled,
+ & .rdtPicker td.rdtDisabled:hover {
+ background: none;
+ color: #999999;
+ cursor: not-allowed;
+ }
+
+ & .rdtPicker td span.rdtOld {
+ color: #999999;
+ }
+
+ & .rdtPicker td span.rdtDisabled,
+ & .rdtPicker td span.rdtDisabled:hover {
+ background: none;
+ color: #999999;
+ cursor: not-allowed;
+ }
+
+ & .rdtPicker th {
+ border-bottom: 1px solid #f9f9f9;
+ }
+
+ & .rdtPicker .dow {
+ width: 14.2857%;
+ border-bottom: none;
+ }
+
+ & .rdtPicker th.rdtSwitch {
+ width: 100px;
+ }
+
+ & .rdtPicker th.rdtNext,
+ & .rdtPicker th.rdtPrev {
+ vertical-align: top;
+ font-size: 21px;
+ }
+
+ & .rdtPrev span,
+ & .rdtNext span {
+ display: block;
+ -webkit-user-select: none; /* Chrome/Safari/Opera */
+ -khtml-user-select: none; /* Konqueror */
+ -moz-user-select: none; /* Firefox */
+ -ms-user-select: none; /* Internet Explorer/Edge */
+ user-select: none;
+ -webkit-touch-callout: none; /* iOS Safari */
+ }
+
+ & .rdtPicker th.rdtDisabled,
+ & .rdtPicker th.rdtDisabled:hover {
+ background: none;
+ color: #999999;
+ cursor: not-allowed;
+ }
+
+ & .rdtPicker thead tr:first-child th {
+ cursor: pointer;
+ }
+
+ & .rdtPicker thead tr:first-child th:hover {
+ background: #eeeeee;
+ }
+
+ & .rdtPicker tfoot {
+ border-top: 1px solid #f9f9f9;
+ }
+
+ & .rdtPicker button {
border: none;
+ background: none;
+ cursor: pointer;
+ }
+
+ & .rdtPicker button:hover {
+ background-color: #eee;
+ }
+
+ & .rdtPicker thead button {
+ width: 100%;
+ height: 100%;
+ }
+
+ & td.rdtMonth,
+ & td.rdtYear {
+ width: 25%;
+ height: 50px;
+ cursor: pointer;
+ }
+
+ & td.rdtMonth:hover,
+ & td.rdtYear:hover {
+ background: #eee;
+ }
+
+ & .rdtCounters {
+ display: inline-block;
+ }
+
+ & .rdtCounters > div {
+ float: left;
+ }
+
+ & .rdtCounter {
+ height: 100px;
+ }
+
+ & .rdtCounter {
+ width: 40px;
+ }
+
+ & .rdtCounterSeparator {
+ line-height: 100px;
+ }
+
+ & .rdtCounter .rdtBtn {
+ display: block;
+ height: 40%;
+ line-height: 40px;
+ cursor: pointer;
+ -webkit-user-select: none; /* Chrome/Safari/Opera */
+ -khtml-user-select: none; /* Konqueror */
+ -moz-user-select: none; /* Firefox */
+ -ms-user-select: none; /* Internet Explorer/Edge */
+ user-select: none;
+ -webkit-touch-callout: none; /* iOS Safari */
+ }
+
+ & .rdtCounter .rdtBtn:hover {
+ background: #eee;
+ }
+
+ & .rdtCounter .rdtCount {
+ height: 20%;
+ font-size: 1.2em;
+ }
+
+ & .rdtMilli {
+ padding-left: 8px;
+ width: 48px;
+ vertical-align: middle;
+ }
+
+ & .rdtMilli input {
+ margin-top: 37px;
+ width: 100%;
+ font-size: 1.2em;
}
}
-
-.main {
- padding-top: 54px;
-}
-
-.notifsContainer {
- position: fixed;
- top: 60px;
- right: 0;
- bottom: 60px;
- z-index: var(--topmostZindex);
- width: 360px;
- pointer-events: none;
-}
-
diff --git a/src/containers/editorialWorkflow/CollectionPageHOC.js b/src/containers/editorialWorkflow/CollectionPageHOC.js
index d75439e4..a557a8bd 100644
--- a/src/containers/editorialWorkflow/CollectionPageHOC.js
+++ b/src/containers/editorialWorkflow/CollectionPageHOC.js
@@ -63,6 +63,6 @@ export default function CollectionPageHOC(CollectionPage) {
return connect(mapStateToProps, {
updateUnpublishedEntryStatus,
- publishUnpublishedEntry
+ publishUnpublishedEntry,
})(CollectionPageHOC);
}
diff --git a/src/reducers/combinedReducer.js b/src/reducers/combinedReducer.js
index d4ee485e..3c247402 100644
--- a/src/reducers/combinedReducer.js
+++ b/src/reducers/combinedReducer.js
@@ -1,10 +1,11 @@
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { reducer as notifReducer } from 'redux-notifications';
+import optimist from 'redux-optimist';
import reducers from '.';
-export default combineReducers({
+export default optimist(combineReducers({
...reducers,
notifs: notifReducer,
routing: routerReducer,
-});
+}));
diff --git a/src/reducers/editorialWorkflow.js b/src/reducers/editorialWorkflow.js
index 246656f0..42b883aa 100644
--- a/src/reducers/editorialWorkflow.js
+++ b/src/reducers/editorialWorkflow.js
@@ -5,8 +5,8 @@ import {
UNPUBLISHED_ENTRY_SUCCESS,
UNPUBLISHED_ENTRIES_REQUEST,
UNPUBLISHED_ENTRIES_SUCCESS,
- UNPUBLISHED_ENTRY_STATUS_CHANGE_SUCCESS,
- UNPUBLISHED_ENTRY_PUBLISH_SUCCESS
+ UNPUBLISHED_ENTRY_STATUS_CHANGE_REQUEST,
+ UNPUBLISHED_ENTRY_PUBLISH_REQUEST,
} from '../actions/editorialWorkflow';
import { CONFIG_SUCCESS } from '../actions/config';
@@ -21,11 +21,11 @@ const unpublishedEntries = (state = null, action) => {
return state;
}
case UNPUBLISHED_ENTRY_REQUEST:
- return state.setIn(['entities', `${action.payload.status}.${action.payload.slug}`, 'isFetching'], true);
+ return state.setIn(['entities', `${ action.payload.status }.${ action.payload.slug }`, 'isFetching'], true);
case UNPUBLISHED_ENTRY_SUCCESS:
return state.setIn(
- ['entities', `${action.payload.status}.${action.payload.entry.slug}`],
+ ['entities', `${ action.payload.status }.${ action.payload.entry.slug }`],
fromJS(action.payload.entry)
);
@@ -36,30 +36,32 @@ const unpublishedEntries = (state = null, action) => {
case UNPUBLISHED_ENTRIES_SUCCESS:
const { entries, pages } = action.payload;
return state.withMutations((map) => {
- entries.forEach((entry) => (
- map.setIn(['entities', `${entry.metaData.status}.${entry.slug}`], fromJS(entry).set('isFetching', false))
+ entries.forEach(entry => (
+ map.setIn(['entities', `${ entry.metaData.status }.${ entry.slug }`], fromJS(entry).set('isFetching', false))
));
map.set('pages', Map({
...pages,
- ids: List(entries.map((entry) => entry.slug))
+ ids: List(entries.map(entry => entry.slug)),
}));
});
- case UNPUBLISHED_ENTRY_STATUS_CHANGE_SUCCESS:
+ case UNPUBLISHED_ENTRY_STATUS_CHANGE_REQUEST:
+ // Update Optimistically
return state.withMutations((map) => {
- let entry = map.getIn(['entities', `${action.payload.oldStatus}.${action.payload.slug}`]);
+ let entry = map.getIn(['entities', `${ action.payload.oldStatus }.${ action.payload.slug }`]);
entry = entry.setIn(['metaData', 'status'], action.payload.newStatus);
let entities = map.get('entities').filter((val, key) => (
- key !== `${action.payload.oldStatus}.${action.payload.slug}`
+ key !== `${ action.payload.oldStatus }.${ action.payload.slug }`
));
- entities = entities.set(`${action.payload.newStatus}.${action.payload.slug}`, entry);
+ entities = entities.set(`${ action.payload.newStatus }.${ action.payload.slug }`, entry);
map.set('entities', entities);
});
- case UNPUBLISHED_ENTRY_PUBLISH_SUCCESS:
- return state.deleteIn(['entities', `${action.payload.status}.${action.payload.slug}`]);
+ case UNPUBLISHED_ENTRY_PUBLISH_REQUEST:
+ // Update Optimistically
+ return state.deleteIn(['entities', `${ action.payload.status }.${ action.payload.slug }`]);
default:
return state;
@@ -67,7 +69,7 @@ const unpublishedEntries = (state = null, action) => {
};
export const selectUnpublishedEntry = (state, status, slug) => {
- return state && state.getIn(['entities', `${status}.${slug}`]);
+ return state && state.getIn(['entities', `${ status }.${ slug }`]);
};
export const selectUnpublishedEntries = (state, status) => {
diff --git a/src/reducers/index.js b/src/reducers/index.js
index 1430bc13..86fc78af 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -2,7 +2,7 @@ import auth from './auth';
import config from './config';
import editor from './editor';
import integrations, * as fromIntegrations from './integrations';
-import entries, * as fromEntries from './entries';
+import entries, * as fromEntries from './entries';
import editorialWorkflow, * as fromEditorialWorkflow from './editorialWorkflow';
import entryDraft from './entryDraft';
import collections from './collections';
@@ -17,7 +17,7 @@ const reducers = {
entries,
editorialWorkflow,
entryDraft,
- medias
+ medias,
};
export default reducers;
@@ -31,7 +31,7 @@ export const selectEntry = (state, collection, slug) =>
export const selectEntries = (state, collection) =>
fromEntries.selectEntries(state.entries, collection);
-export const selectSearchedEntries = (state) =>
+export const selectSearchedEntries = state =>
fromEntries.selectSearchedEntries(state.entries);
export const selectUnpublishedEntry = (state, status, slug) =>