fix small code issues in RTE implementation
This commit is contained in:
parent
3d83325afc
commit
9c0b7262ef
@ -231,20 +231,25 @@ export function persistUnpublishedEntry(collection, existingUnpublishedEntry) {
|
|||||||
const transactionID = uuid.v4();
|
const transactionID = uuid.v4();
|
||||||
const assetProxies = entryDraft.get('mediaFiles').map(path => getAsset(state, path));
|
const assetProxies = entryDraft.get('mediaFiles').map(path => getAsset(state, path));
|
||||||
const entry = entryDraft.get('entry');
|
const entry = entryDraft.get('entry');
|
||||||
const transformedData = serializeValues(entryDraft.getIn(['entry', 'data']), collection.get('fields'));
|
|
||||||
const transformedEntry = entry.set('data', transformedData);
|
|
||||||
const transformedEntryDraft = entryDraft.set('entry', transformedEntry);
|
|
||||||
|
|
||||||
dispatch(unpublishedEntryPersisting(collection, transformedEntry, transactionID));
|
/**
|
||||||
|
* Serialize the values of any fields with registered serializers, and
|
||||||
|
* update the entry and entryDraft with the serialized values.
|
||||||
|
*/
|
||||||
|
const serializedData = serializeValues(entryDraft.getIn(['entry', 'data']), collection.get('fields'));
|
||||||
|
const serializedEntry = entry.set('data', serializedData);
|
||||||
|
const serializedEntryDraft = entryDraft.set('entry', serializedEntry);
|
||||||
|
|
||||||
|
dispatch(unpublishedEntryPersisting(collection, serializedEntry, transactionID));
|
||||||
const persistAction = existingUnpublishedEntry ? backend.persistUnpublishedEntry : backend.persistEntry;
|
const persistAction = existingUnpublishedEntry ? backend.persistUnpublishedEntry : backend.persistEntry;
|
||||||
return persistAction.call(backend, state.config, collection, transformedEntryDraft, assetProxies.toJS())
|
return persistAction.call(backend, state.config, collection, serializedEntryDraft, assetProxies.toJS())
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch(notifSend({
|
dispatch(notifSend({
|
||||||
message: 'Entry saved',
|
message: 'Entry saved',
|
||||||
kind: 'success',
|
kind: 'success',
|
||||||
dismissAfter: 4000,
|
dismissAfter: 4000,
|
||||||
}));
|
}));
|
||||||
return dispatch(unpublishedEntryPersisted(collection, transformedEntry, transactionID));
|
return dispatch(unpublishedEntryPersisted(collection, serializedEntry, transactionID));
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
dispatch(notifSend({
|
dispatch(notifSend({
|
||||||
|
@ -271,19 +271,24 @@ export function persistEntry(collection) {
|
|||||||
const backend = currentBackend(state.config);
|
const backend = currentBackend(state.config);
|
||||||
const assetProxies = entryDraft.get('mediaFiles').map(path => getAsset(state, path));
|
const assetProxies = entryDraft.get('mediaFiles').map(path => getAsset(state, path));
|
||||||
const entry = entryDraft.get('entry');
|
const entry = entryDraft.get('entry');
|
||||||
const transformedData = serializeValues(entryDraft.getIn(['entry', 'data']), collection.get('fields'));
|
|
||||||
const transformedEntry = entry.set('data', transformedData);
|
/**
|
||||||
const transformedEntryDraft = entryDraft.set('entry', transformedEntry);
|
* Serialize the values of any fields with registered serializers, and
|
||||||
dispatch(entryPersisting(collection, transformedEntry));
|
* update the entry and entryDraft with the serialized values.
|
||||||
|
*/
|
||||||
|
const serializedData = serializeValues(entryDraft.getIn(['entry', 'data']), collection.get('fields'));
|
||||||
|
const serializedEntry = entry.set('data', serializedData);
|
||||||
|
const serializedEntryDraft = entryDraft.set('entry', serializedEntry);
|
||||||
|
dispatch(entryPersisting(collection, serializedEntry));
|
||||||
return backend
|
return backend
|
||||||
.persistEntry(state.config, collection, transformedEntryDraft, assetProxies.toJS())
|
.persistEntry(state.config, collection, serializedEntryDraft, assetProxies.toJS())
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch(notifSend({
|
dispatch(notifSend({
|
||||||
message: 'Entry saved',
|
message: 'Entry saved',
|
||||||
kind: 'success',
|
kind: 'success',
|
||||||
dismissAfter: 4000,
|
dismissAfter: 4000,
|
||||||
}));
|
}));
|
||||||
return dispatch(entryPersisted(collection, transformedEntry));
|
return dispatch(entryPersisted(collection, serializedEntry));
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -292,7 +297,7 @@ export function persistEntry(collection) {
|
|||||||
kind: 'danger',
|
kind: 'danger',
|
||||||
dismissAfter: 8000,
|
dismissAfter: 8000,
|
||||||
}));
|
}));
|
||||||
return dispatch(entryPersistFail(collection, transformedEntry, error));
|
return dispatch(entryPersistFail(collection, serializedEntry, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,10 @@ const style = {
|
|||||||
fontFamily: 'Roboto, "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif',
|
fontFamily: 'Roboto, "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use a stateful component so that child components can effectively utilize
|
||||||
|
* `shouldComponentUpdate`.
|
||||||
|
*/
|
||||||
export default class Preview extends React.Component {
|
export default class Preview extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { collection, fields, widgetFor } = this.props;
|
const { collection, fields, widgetFor } = this.props;
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { ScrollSyncPane } from '../ScrollSync';
|
import { ScrollSyncPane } from '../ScrollSync';
|
||||||
|
|
||||||
// We need to create a lightweight component here so that we can
|
/**
|
||||||
// access the context within the Frame. This allows us to attach
|
* We need to create a lightweight component here so that we can access the
|
||||||
// the ScrollSyncPane to the body.
|
* context within the Frame. This allows us to attach the ScrollSyncPane to the
|
||||||
|
* body.
|
||||||
|
*/
|
||||||
class PreviewContent extends React.Component {
|
class PreviewContent extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { previewComponent, previewProps } = this.props;
|
const { previewComponent, previewProps } = this.props;
|
||||||
|
@ -17,6 +17,9 @@ export default class PreviewPane extends React.Component {
|
|||||||
const { fieldsMetaData, getAsset, entry } = props;
|
const { fieldsMetaData, getAsset, entry } = props;
|
||||||
const widget = resolveWidget(field.get('widget'));
|
const widget = resolveWidget(field.get('widget'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use an HOC to provide conditional updates for all previews.
|
||||||
|
*/
|
||||||
return !widget.preview ? null : (
|
return !widget.preview ? null : (
|
||||||
<PreviewHOC
|
<PreviewHOC
|
||||||
previewComponent={widget.preview}
|
previewComponent={widget.preview}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||||
import isEqual from 'lodash/isEqual';
|
|
||||||
|
|
||||||
const truthy = () => ({ error: false });
|
const truthy = () => ({ error: false });
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import mdastDefinitions from 'mdast-util-definitions';
|
|||||||
* Yields:
|
* Yields:
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* ![alpha][http://example.com/example.jpg]
|
* 
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
class PreviewHOC extends React.Component {
|
class PreviewHOC extends React.Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only re-render on value change, but always re-render objects and lists.
|
||||||
|
* Their child widgets will each also be wrapped with this component, and
|
||||||
|
* will only be updated on value change.
|
||||||
|
*/
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
// Only re-render on value change, but always re-render objects and lists.
|
|
||||||
// Their child widgets will each also be wrapped with this component, and
|
|
||||||
// will only be updated on value change.
|
|
||||||
const isWidgetContainer = ['object', 'list'].includes(nextProps.field.get('widget'));
|
const isWidgetContainer = ['object', 'list'].includes(nextProps.field.get('widget'));
|
||||||
return isWidgetContainer || this.props.value !== nextProps.value;
|
return isWidgetContainer || this.props.value !== nextProps.value;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,11 @@ class EntryPage extends React.Component {
|
|||||||
const { entry, newEntry, fields, collection } = nextProps;
|
const { entry, newEntry, fields, collection } = nextProps;
|
||||||
|
|
||||||
if (entry && !entry.get('isFetching') && !entry.get('error')) {
|
if (entry && !entry.get('isFetching') && !entry.get('error')) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize entry values for widgets with registered serializers before
|
||||||
|
* creating the entry draft.
|
||||||
|
*/
|
||||||
const values = deserializeValues(entry.get('data'), fields);
|
const values = deserializeValues(entry.get('data'), fields);
|
||||||
const deserializedEntry = entry.set('data', values);
|
const deserializedEntry = entry.set('data', values);
|
||||||
this.createDraft(deserializedEntry);
|
this.createDraft(deserializedEntry);
|
||||||
|
@ -20,22 +20,40 @@ import registry from './registry';
|
|||||||
* registered deserialization handlers run on entry load, and serialization
|
* registered deserialization handlers run on entry load, and serialization
|
||||||
* handlers run on persist.
|
* handlers run on persist.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const runSerializer = (values, fields, method) => {
|
const runSerializer = (values, fields, method) => {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduce the list of fields to a map where keys are field names and values
|
||||||
|
* are field values, serializing the values of fields whose widgets have
|
||||||
|
* registered serializers. If the field is a list or object, call recursively
|
||||||
|
* for nested fields.
|
||||||
|
*/
|
||||||
return fields.reduce((acc, field) => {
|
return fields.reduce((acc, field) => {
|
||||||
const fieldName = field.get('name');
|
const fieldName = field.get('name');
|
||||||
const value = values.get(fieldName);
|
const value = values.get(fieldName);
|
||||||
const serializer = registry.getWidgetValueSerializer(field.get('widget'));
|
const serializer = registry.getWidgetValueSerializer(field.get('widget'));
|
||||||
const nestedFields = field.get('fields');
|
const nestedFields = field.get('fields');
|
||||||
|
|
||||||
|
// Call recursively for fields within lists
|
||||||
if (nestedFields && List.isList(value)) {
|
if (nestedFields && List.isList(value)) {
|
||||||
return acc.set(fieldName, value.map(val => runSerializer(val, nestedFields, method)));
|
return acc.set(fieldName, value.map(val => runSerializer(val, nestedFields, method)));
|
||||||
} else if (nestedFields && Map.isMap(value)) {
|
}
|
||||||
|
|
||||||
|
// Call recursively for fields within objects
|
||||||
|
if (nestedFields && Map.isMap(value)) {
|
||||||
return acc.set(fieldName, runSerializer(value, nestedFields, method));
|
return acc.set(fieldName, runSerializer(value, nestedFields, method));
|
||||||
} else if (serializer && !isNil(value)) {
|
}
|
||||||
|
|
||||||
|
// Run serialization method on value if not null or undefined
|
||||||
|
if (serializer && !isNil(value)) {
|
||||||
return acc.set(fieldName, serializer[method](value));
|
return acc.set(fieldName, serializer[method](value));
|
||||||
} else if (!isNil(value)) {
|
}
|
||||||
|
|
||||||
|
// If no serializer is registered for the field's widget, use the field as is
|
||||||
|
if (!isNil(value)) {
|
||||||
return acc.set(fieldName, value);
|
return acc.set(fieldName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, Map());
|
}, Map());
|
||||||
};
|
};
|
||||||
|
@ -21,11 +21,10 @@ const entries = (state = Map({ entities: Map(), pages: Map() }), action) => {
|
|||||||
return state.setIn(['entities', `${ action.payload.collection }.${ action.payload.slug }`, 'isFetching'], true);
|
return state.setIn(['entities', `${ action.payload.collection }.${ action.payload.slug }`, 'isFetching'], true);
|
||||||
|
|
||||||
case ENTRY_SUCCESS:
|
case ENTRY_SUCCESS:
|
||||||
const result = state.setIn(
|
return state.setIn(
|
||||||
['entities', `${ action.payload.collection }.${ action.payload.entry.slug }`],
|
['entities', `${ action.payload.collection }.${ action.payload.entry.slug }`],
|
||||||
fromJS(action.payload.entry)
|
fromJS(action.payload.entry)
|
||||||
);
|
);
|
||||||
return result;
|
|
||||||
|
|
||||||
case ENTRIES_REQUEST:
|
case ENTRIES_REQUEST:
|
||||||
return state.setIn(['pages', action.payload.collection, 'isFetching'], true);
|
return state.setIn(['pages', action.payload.collection, 'isFetching'], true);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user