Skip to content

Commit 78df979

Browse files
committed
allow multiple pieces of persistence for one prop
1 parent 42428a6 commit 78df979

File tree

1 file changed

+76
-50
lines changed

1 file changed

+76
-50
lines changed

Diff for: dash-renderer/src/persistence.js

+76-50
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@
4343
* }
4444
* }
4545
* - `extract` turns a prop value into a reduced value to store.
46-
* - `apply` puts an extracted value back into the prop.
46+
* - `apply` puts an extracted value back into the prop. Make sure this creates
47+
* a new object rather than mutating `proValue`, and that if there are
48+
* multiple `piece` entries for one `propName`, their `apply` functions
49+
* commute - which should not be an issue if they extract and apply
50+
* non-intersecting parts of the full prop.
4751
* You only need to define these for the props that need them.
4852
* It's important that `extract` pulls out *only* the relevant pieces of the
4953
* prop, because persistence is only maintained if the extracted value of the
@@ -52,13 +56,13 @@
5256
*/
5357

5458
import {
55-
difference,
5659
equals,
5760
filter,
5861
forEach,
5962
keys,
6063
lensPath,
6164
set,
65+
symmetricDifference,
6266
type,
6367
} from 'ramda';
6468

@@ -159,10 +163,12 @@ const noopTransform = {
159163
apply: (storedValue, _propValue) => storedValue,
160164
};
161165

162-
const getTransform = (element, propName) =>
163-
(element.persistenceTransforms || {})[propName] || noopTransform;
166+
const getTransform = (element, propName, propPart) =>
167+
propPart
168+
? element.persistenceTransforms[propName][propPart]
169+
: noopTransform;
164170

165-
const getNewValKey = (id, propName) => id + '.' + propName;
171+
const getNewValKey = (id, persistedProp) => id + '.' + persistedProp;
166172
const getOriginalValKey = newValKey => newValKey + '.orig';
167173
const getPersistIdKey = newValKey => newValKey + '.id';
168174

@@ -202,41 +208,37 @@ export function recordUiEdit(layout, newProps) {
202208
return;
203209
}
204210

205-
forEach(propName => {
206-
// TODO: this only supports specifying top-level props to persist
207-
// DO we need nested specification?
208-
// This *does* support custom methods to save/restore these props,
209-
// so if we persist `columns` on a table, the component can specify
210-
// to only keep & restore the names, associating them with IDs.
211-
// It just wouldn't allow us to separately enable/disable persisting
212-
// something else inside columns.
211+
forEach(persistedProp => {
212+
const [propName, propPart] = persistedProp.split('.');
213213
if (newProps[propName]) {
214214
const storage = stores[persistence_type];
215-
const transform = getTransform(element, propName);
215+
const {extract} = getTransform(element, propName, propPart);
216216

217-
const newValKey = getNewValKey(id, propName);
217+
const newValKey = getNewValKey(id, persistedProp);
218218
const persistIdKey = getPersistIdKey(newValKey);
219-
const setOriginalAndId = () => {
220-
storage.setItem(
221-
getOriginalValKey(newValKey),
222-
transform.extract(props[propName])
223-
);
224-
storage.setItem(persistIdKey, persistence);
225-
};
226-
if (
227-
!storage.hasItem(newValKey) ||
228-
storage.getItem(persistIdKey) !== persistence
229-
) {
230-
setOriginalAndId();
219+
const previousVal = extract(props[propName]);
220+
const newVal = extract(newProps[propName]);
221+
222+
// mainly for nested props with multiple persisted parts, it's
223+
// possible to have the same value as before - should not store
224+
// in this case.
225+
if (previousVal !== newVal) {
226+
if (
227+
!storage.hasItem(newValKey) ||
228+
storage.getItem(persistIdKey) !== persistence
229+
) {
230+
storage.setItem(getOriginalValKey(newValKey), previousVal);
231+
storage.setItem(persistIdKey, persistence);
232+
}
233+
storage.setItem(newValKey, newVal);
231234
}
232-
storage.setItem(newValKey, transform.extract(newProps[propName]));
233235
}
234236
}, persisted_props);
235237
}
236238

237-
function clearUIEdit(id, persistence_type, propName) {
239+
function clearUIEdit(id, persistence_type, persistedProp) {
238240
const storage = stores[persistence_type];
239-
const newValKey = getNewValKey(id, propName);
241+
const newValKey = getNewValKey(id, persistedProp);
240242

241243
if (storage.hasItem(newValKey)) {
242244
storage.removeItem(newValKey);
@@ -270,10 +272,13 @@ function persistenceMods(layout, component, path) {
270272
let layoutOut = layout;
271273
if (persistence) {
272274
const storage = stores[persistence_type];
273-
forEach(propName => {
274-
const newValKey = getNewValKey(id, propName);
275+
const update = {};
276+
forEach(persistedProp => {
277+
const [propName, propPart] = persistedProp.split('.');
278+
const newValKey = getNewValKey(id, persistedProp);
275279
const storedPersistID = storage.getItem(getPersistIdKey(newValKey));
276-
const transform = getTransform(element, propName);
280+
const transform = getTransform(element, propName, propPart);
281+
277282
if (storedPersistID) {
278283
if (
279284
storedPersistID === persistence &&
@@ -282,19 +287,25 @@ function persistenceMods(layout, component, path) {
282287
transform.extract(props[propName])
283288
)
284289
) {
285-
layoutOut = set(
286-
lensPath(path.concat('props', propName)),
287-
transform.apply(
288-
storage.getItem(newValKey),
289-
props[propName]
290-
),
291-
layoutOut
290+
// To handle multiple nested props, apply each stored value
291+
// in turn; then at the end we'll push these into the layout
292+
update[propName] = transform.apply(
293+
storage.getItem(newValKey),
294+
propName in update ? update[propName] : props[propName]
292295
);
293296
} else {
294-
clearUIEdit(id, persistence_type, propName);
297+
clearUIEdit(id, persistence_type, persistedProp);
295298
}
296299
}
297300
}, persisted_props);
301+
302+
for (const propName in update) {
303+
layoutOut = set(
304+
lensPath(path.concat('props', propName)),
305+
update[propName],
306+
layoutOut
307+
);
308+
}
298309
}
299310

300311
// recurse inward
@@ -326,9 +337,13 @@ function persistenceMods(layout, component, path) {
326337
* but not for props nested inside children
327338
*/
328339
export function prunePersistence(layout, newProps) {
329-
const {id, persistence, persisted_props, persistence_type} = getProps(
330-
layout
331-
);
340+
const {
341+
id,
342+
persistence,
343+
persisted_props,
344+
persistence_type,
345+
element,
346+
} = getProps(layout);
332347
if (!persistence) {
333348
return;
334349
}
@@ -343,15 +358,26 @@ export function prunePersistence(layout, newProps) {
343358
return;
344359
}
345360

361+
// if the persisted props list itself changed, clear any props not
362+
// present in both the new and old
346363
if ('persisted_props' in newProps) {
347364
forEach(
348-
prevPropName => clearUIEdit(id, persistence_type, prevPropName),
349-
difference(persisted_props, newProps.persisted_props)
365+
persistedProp => clearUIEdit(id, persistence_type, persistedProp),
366+
symmetricDifference(persisted_props, newProps.persisted_props)
350367
);
351368
}
352369

353-
forEach(
354-
propName => clearUIEdit(id, persistence_type, propName),
355-
difference(keys(newProps), persisted_props)
356-
);
370+
// now the main point - clear any edit associated with a prop that changed
371+
// note that this is independent of the new prop value.
372+
const transforms = element.persistenceTransforms || {};
373+
for (const propName in newProps) {
374+
const propTransforms = transforms[propName];
375+
if (propTransforms) {
376+
for (const propPart in propTransforms) {
377+
clearUIEdit(id, persistence_type, `${propName}.${propPart}`);
378+
}
379+
} else {
380+
clearUIEdit(id, persistence_type, propName);
381+
}
382+
}
357383
}

0 commit comments

Comments
 (0)