Skip to content

Commit 7a21285

Browse files
committed
persistence initial commit
1 parent 5c916fe commit 7a21285

File tree

4 files changed

+420
-54
lines changed

4 files changed

+420
-54
lines changed

dash-renderer/src/APIController.react.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
hydrateInitialOutputs,
1111
setLayout,
1212
} from './actions/index';
13+
import {applyPersistence} from './persistence';
1314
import apiThunk from './actions/api';
1415
import {getAppState} from './reducers/constants';
1516
import {STATUS} from './constants/constants';
@@ -48,7 +49,7 @@ class UnconnectedContainer extends Component {
4849
dispatch(apiThunk('_dash-layout', 'GET', 'layoutRequest'));
4950
} else if (layoutRequest.status === STATUS.OK) {
5051
if (isEmpty(layout)) {
51-
dispatch(setLayout(layoutRequest.content));
52+
dispatch(setLayout(applyPersistence(layoutRequest.content)));
5253
} else if (isNil(paths)) {
5354
dispatch(computePaths({subTree: layout, startingPath: []}));
5455
}

dash-renderer/src/TreeContainer.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
type,
2323
} from 'ramda';
2424
import {notifyObservers, updateProps} from './actions';
25+
import {recordUiEdit} from './persistence';
2526
import ComponentErrorBoundary from './components/error/ComponentErrorBoundary.react';
2627
import checkPropTypes from 'check-prop-types';
2728

@@ -164,6 +165,7 @@ class TreeContainer extends Component {
164165
_dashprivate_dependencies,
165166
_dashprivate_dispatch,
166167
_dashprivate_path,
168+
_dashprivate_layout,
167169
} = this.props;
168170

169171
const id = this.getLayoutProps().id;
@@ -185,6 +187,10 @@ class TreeContainer extends Component {
185187
)
186188
)(keysIn(newProps));
187189

190+
// setProps here is triggered by the UI - record these changes
191+
// for persistence
192+
recordUiEdit(_dashprivate_layout, newProps);
193+
188194
// Always update this component's props
189195
_dashprivate_dispatch(
190196
updateProps({

dash-renderer/src/actions/index.js

Lines changed: 66 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
lensPath,
1717
mergeLeft,
1818
mergeDeepRight,
19+
path,
1920
pluck,
2021
propEq,
2122
reject,
@@ -31,6 +32,7 @@ import {getAction} from './constants';
3132
import cookie from 'cookie';
3233
import {uid, urlBase, isMultiOutputProp, parseMultipleOutputs} from '../utils';
3334
import {STATUS} from '../constants/constants';
35+
import {applyPersistence, prunePersistence} from '../persistence';
3436

3537
export const updateProps = createAction(getAction('ON_PROP_CHANGE'));
3638
export const setRequestQueue = createAction(getAction('SET_REQUEST_QUEUE'));
@@ -530,6 +532,32 @@ function updateOutput(
530532
});
531533
}
532534

535+
function doUpdateProps(id, updatedProps) {
536+
const {layout, paths} = getState();
537+
const itempath = paths[id];
538+
if (!itempath) {
539+
return false;
540+
}
541+
542+
// This is a callback-generated update.
543+
// Check if this invalidates existing persisted prop values,
544+
prunePersistence(path(itempath, layout), updatedProps);
545+
546+
// In case the update contains whole components, see if any of
547+
// those components have props to update to persist user edits.
548+
const finalProps = applyPersistence(updatedProps);
549+
550+
dispatch(
551+
updateProps({
552+
itempath,
553+
props: finalProps,
554+
source: 'response',
555+
})
556+
);
557+
558+
return finalProps;
559+
}
560+
533561
// Clientside hook
534562
if (clientside_function) {
535563
let returnValue;
@@ -587,24 +615,20 @@ function updateOutput(
587615
updateRequestQueue(false, STATUS.OK);
588616

589617
// Update the layout with the new result
590-
dispatch(
591-
updateProps({
592-
itempath: getState().paths[outputId],
593-
props: updatedProps,
594-
source: 'response',
595-
})
596-
);
618+
const appliedProps = doUpdateProps(outputId, updatedProps);
597619

598620
/*
599621
* This output could itself be a serverside or clientside input
600622
* to another function
601623
*/
602-
dispatch(
603-
notifyObservers({
604-
id: outputId,
605-
props: updatedProps,
606-
})
607-
);
624+
if (appliedProps) {
625+
dispatch(
626+
notifyObservers({
627+
id: outputId,
628+
props: appliedProps,
629+
})
630+
);
631+
}
608632
}
609633

610634
if (isMultiOutputProp(payload.output)) {
@@ -717,20 +741,16 @@ function updateOutput(
717741
const handleResponse = ([outputIdAndProp, props]) => {
718742
// Backward compatibility
719743
const pathKey = multi ? outputIdAndProp : outputComponentId;
720-
const observerUpdatePayload = {
721-
itempath: getState().paths[pathKey],
722-
props,
723-
source: 'response',
724-
};
725-
if (!observerUpdatePayload.itempath) {
744+
745+
const appliedProps = doUpdateProps(pathKey, props);
746+
if (!appliedProps) {
726747
return;
727748
}
728-
dispatch(updateProps(observerUpdatePayload));
729749

730750
dispatch(
731751
notifyObservers({
732752
id: pathKey,
733-
props: props,
753+
props: appliedProps,
734754
})
735755
);
736756

@@ -739,10 +759,11 @@ function updateOutput(
739759
* paths store.
740760
* TODO - Do we need to wait for updateProps to finish?
741761
*/
742-
if (has('children', observerUpdatePayload.props)) {
762+
if (has('children', appliedProps)) {
763+
const newChildren = appliedProps.children;
743764
dispatch(
744765
computePaths({
745-
subTree: observerUpdatePayload.props.children,
766+
subTree: newChildren,
746767
startingPath: concat(
747768
getState().paths[pathKey],
748769
['props', 'children']
@@ -756,11 +777,8 @@ function updateOutput(
756777
* new children components
757778
*/
758779
if (
759-
contains(
760-
type(observerUpdatePayload.props.children),
761-
['Array', 'Object']
762-
) &&
763-
!isEmpty(observerUpdatePayload.props.children)
780+
contains(type(newChildren), ['Array', 'Object']) &&
781+
!isEmpty(newChildren)
764782
) {
765783
/*
766784
* TODO: We're just naively crawling
@@ -770,32 +788,27 @@ function updateOutput(
770788
* to compute the subtree
771789
*/
772790
const newProps = {};
773-
crawlLayout(
774-
observerUpdatePayload.props.children,
775-
function appendIds(child) {
776-
if (hasId(child)) {
777-
keys(child.props).forEach(childProp => {
778-
const componentIdAndProp = `${child.props.id}.${childProp}`;
779-
if (
780-
has(
781-
componentIdAndProp,
782-
InputGraph.nodes
783-
)
784-
) {
785-
newProps[componentIdAndProp] = {
786-
id: child.props.id,
787-
props: {
788-
[childProp]:
789-
child.props[
790-
childProp
791-
],
792-
},
793-
};
794-
}
795-
});
796-
}
791+
crawlLayout(newChildren, function appendIds(child) {
792+
if (hasId(child)) {
793+
keys(child.props).forEach(childProp => {
794+
const componentIdAndProp = `${child.props.id}.${childProp}`;
795+
if (
796+
has(
797+
componentIdAndProp,
798+
InputGraph.nodes
799+
)
800+
) {
801+
newProps[componentIdAndProp] = {
802+
id: child.props.id,
803+
props: {
804+
[childProp]:
805+
child.props[childProp],
806+
},
807+
};
808+
}
809+
});
797810
}
798-
);
811+
});
799812

800813
/*
801814
* Organize props by shared outputs so that we

0 commit comments

Comments
 (0)