react-native-onyx 1.0.53 → 1.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/web.development.js +111 -74
- package/dist/web.development.js.map +1 -1
- package/dist/web.min.js +1 -1
- package/dist/web.min.js.map +1 -1
- package/lib/Onyx.js +88 -62
- package/lib/metrics/index.native.js +1 -0
- package/lib/storage/__mocks__/index.js +6 -5
- package/lib/storage/providers/LocalForage.js +21 -10
- package/lib/storage/providers/SQLiteStorage.js +14 -4
- package/package.json +2 -2
package/dist/web.development.js
CHANGED
|
@@ -102,6 +102,9 @@ const METHOD = {
|
|
|
102
102
|
CLEAR: 'clear'
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
// Key/value store of Onyx key and arrays of values to merge
|
|
106
|
+
const mergeQueue = {};
|
|
107
|
+
|
|
105
108
|
// Keeps track of the last connectionID that was used so we can keep incrementing it
|
|
106
109
|
let lastConnectionID = 0;
|
|
107
110
|
|
|
@@ -924,6 +927,38 @@ function evictStorageAndRetry(error, onyxMethod) {for (var _len = arguments.leng
|
|
|
924
927
|
then(() => onyxMethod(...args));
|
|
925
928
|
}
|
|
926
929
|
|
|
930
|
+
/**
|
|
931
|
+
* Notifys subscribers and writes current value to cache
|
|
932
|
+
*
|
|
933
|
+
* @param {String} key
|
|
934
|
+
* @param {*} value
|
|
935
|
+
* @param {Boolean} hasChanged
|
|
936
|
+
* @param {String} method
|
|
937
|
+
*/
|
|
938
|
+
function broadcastUpdate(key, value, hasChanged, method) {
|
|
939
|
+
// Logging properties only since values could be sensitive things we don't want to log
|
|
940
|
+
_Logger__WEBPACK_IMPORTED_MODULE_6__.logInfo(`${method}() called for key: ${key}${underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject(value) ? ` properties: ${underscore__WEBPACK_IMPORTED_MODULE_2___default().keys(value).join(',')}` : ''}`);
|
|
941
|
+
|
|
942
|
+
// Update subscribers if the cached value has changed, or when the subscriber specifically requires
|
|
943
|
+
// all updates regardless of value changes (indicated by initWithStoredValues set to false).
|
|
944
|
+
if (hasChanged) {
|
|
945
|
+
_OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].set(key, value);
|
|
946
|
+
} else {
|
|
947
|
+
_OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].addToAccessedKeys(key);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
notifySubscribersOnNextTick(key, value, (subscriber) => hasChanged || subscriber.initWithStoredValues === false);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* @private
|
|
955
|
+
* @param {String} key
|
|
956
|
+
* @returns {Boolean}
|
|
957
|
+
*/
|
|
958
|
+
function hasPendingMergeForKey(key) {
|
|
959
|
+
return Boolean(mergeQueue[key]);
|
|
960
|
+
}
|
|
961
|
+
|
|
927
962
|
/**
|
|
928
963
|
* Write a value to our store with the given key
|
|
929
964
|
*
|
|
@@ -937,24 +972,20 @@ function set(key, value) {
|
|
|
937
972
|
return remove(key);
|
|
938
973
|
}
|
|
939
974
|
|
|
940
|
-
// eslint-disable-next-line no-use-before-define
|
|
941
975
|
if (hasPendingMergeForKey(key)) {
|
|
942
976
|
_Logger__WEBPACK_IMPORTED_MODULE_6__.logAlert(`Onyx.set() called after Onyx.merge() for key: ${key}. It is recommended to use set() or merge() not both.`);
|
|
943
977
|
}
|
|
944
978
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
979
|
+
const hasChanged = _OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].hasValueChanged(key, value);
|
|
980
|
+
|
|
981
|
+
// This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
|
|
982
|
+
broadcastUpdate(key, value, hasChanged, 'set');
|
|
983
|
+
|
|
984
|
+
// If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
|
|
985
|
+
if (!hasChanged) {
|
|
950
986
|
return Promise.resolve();
|
|
951
987
|
}
|
|
952
988
|
|
|
953
|
-
// Adds the key to cache when it's not available
|
|
954
|
-
_OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].set(key, value);
|
|
955
|
-
notifySubscribersOnNextTick(key, value);
|
|
956
|
-
|
|
957
|
-
// Write the thing to persistent storage, which will trigger a storage event for any other tabs open on this domain
|
|
958
989
|
return _storage__WEBPACK_IMPORTED_MODULE_5__["default"].setItem(key, value).
|
|
959
990
|
catch((error) => evictStorageAndRetry(error, set, key, value));
|
|
960
991
|
}
|
|
@@ -992,67 +1023,39 @@ function multiSet(data) {
|
|
|
992
1023
|
catch((error) => evictStorageAndRetry(error, multiSet, data));
|
|
993
1024
|
}
|
|
994
1025
|
|
|
995
|
-
// Key/value store of Onyx key and arrays of values to merge
|
|
996
|
-
const mergeQueue = {};
|
|
997
|
-
|
|
998
|
-
/**
|
|
999
|
-
* @private
|
|
1000
|
-
* @param {String} key
|
|
1001
|
-
* @returns {Boolean}
|
|
1002
|
-
*/
|
|
1003
|
-
function hasPendingMergeForKey(key) {
|
|
1004
|
-
return Boolean(mergeQueue[key]);
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
1026
|
/**
|
|
1008
|
-
*
|
|
1009
|
-
* value updates and return a single value. Merge attempts are
|
|
1010
|
-
* batched. They must occur after a single call to get() so we
|
|
1011
|
-
* can avoid race conditions.
|
|
1027
|
+
* Merges an array of changes with an existing value
|
|
1012
1028
|
*
|
|
1013
1029
|
* @private
|
|
1014
|
-
* @param {
|
|
1015
|
-
* @param {
|
|
1016
|
-
*
|
|
1030
|
+
* @param {*} existingValue
|
|
1031
|
+
* @param {Array<*>} changes Array of changes that should be applied to the existing value
|
|
1017
1032
|
* @returns {*}
|
|
1018
1033
|
*/
|
|
1019
|
-
function applyMerge(
|
|
1020
|
-
const
|
|
1021
|
-
if (underscore__WEBPACK_IMPORTED_MODULE_2___default().isArray(data) || underscore__WEBPACK_IMPORTED_MODULE_2___default().every(mergeValues, (underscore__WEBPACK_IMPORTED_MODULE_2___default().isArray))) {
|
|
1022
|
-
// Array values will always just concatenate
|
|
1023
|
-
// more items onto the end of the array
|
|
1024
|
-
return underscore__WEBPACK_IMPORTED_MODULE_2___default().reduce(mergeValues, (modifiedData, mergeValue) => [
|
|
1025
|
-
...modifiedData,
|
|
1026
|
-
...mergeValue],
|
|
1027
|
-
data || []);
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
if (underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject(data) || underscore__WEBPACK_IMPORTED_MODULE_2___default().every(mergeValues, (underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject))) {
|
|
1031
|
-
// Object values are merged one after the other
|
|
1032
|
-
return underscore__WEBPACK_IMPORTED_MODULE_2___default().reduce(mergeValues, (modifiedData, mergeValue) => {
|
|
1033
|
-
// lodash adds a small overhead so we don't use it here
|
|
1034
|
-
// eslint-disable-next-line prefer-object-spread, rulesdir/prefer-underscore-method
|
|
1035
|
-
const newData = Object.assign({}, (0,_fastMerge__WEBPACK_IMPORTED_MODULE_9__["default"])(modifiedData, mergeValue));
|
|
1034
|
+
function applyMerge(existingValue, changes) {
|
|
1035
|
+
const lastChange = underscore__WEBPACK_IMPORTED_MODULE_2___default().last(changes);
|
|
1036
1036
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1037
|
+
if (underscore__WEBPACK_IMPORTED_MODULE_2___default().isArray(existingValue) || underscore__WEBPACK_IMPORTED_MODULE_2___default().isArray(lastChange)) {
|
|
1038
|
+
return lastChange;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject(existingValue) || underscore__WEBPACK_IMPORTED_MODULE_2___default().every(changes, (underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject))) {
|
|
1042
|
+
// Object values are merged one after the other
|
|
1043
|
+
// lodash adds a small overhead so we don't use it here
|
|
1044
|
+
// eslint-disable-next-line prefer-object-spread, rulesdir/prefer-underscore-method
|
|
1045
|
+
return underscore__WEBPACK_IMPORTED_MODULE_2___default().reduce(changes, (modifiedData, change) => (0,_fastMerge__WEBPACK_IMPORTED_MODULE_9__["default"])(modifiedData, change),
|
|
1046
|
+
existingValue || {});
|
|
1042
1047
|
}
|
|
1043
1048
|
|
|
1044
1049
|
// If we have anything else we can't merge it so we'll
|
|
1045
1050
|
// simply return the last value that was queued
|
|
1046
|
-
return
|
|
1051
|
+
return lastChange;
|
|
1047
1052
|
}
|
|
1048
1053
|
|
|
1049
1054
|
/**
|
|
1050
1055
|
* Merge a new value into an existing value at a key.
|
|
1051
1056
|
*
|
|
1052
|
-
* The types of values that can be merged are `Object` and `Array`. To set another type of value use `Onyx.set()`.
|
|
1053
|
-
*
|
|
1054
|
-
* to note that if you have an array value property on an `Object` that the default behavior of lodash/merge is not to
|
|
1055
|
-
* concatenate. See here: https://github.com/lodash/lodash/issues/2872
|
|
1057
|
+
* The types of values that can be merged are `Object` and `Array`. To set another type of value use `Onyx.set()`.
|
|
1058
|
+
* Values of type `Object` get merged with the old value, whilst for `Array`'s we simply replace the current value with the new one.
|
|
1056
1059
|
*
|
|
1057
1060
|
* Calls to `Onyx.merge()` are batched so that any calls performed in a single tick will stack in a queue and get
|
|
1058
1061
|
* applied in the order they were called. Note: `Onyx.set()` calls do not work this way so use caution when mixing
|
|
@@ -1065,26 +1068,48 @@ function applyMerge(key, data) {
|
|
|
1065
1068
|
* Onyx.merge(ONYXKEYS.POLICY, {name: 'My Workspace'}); // -> {id: 1, name: 'My Workspace'}
|
|
1066
1069
|
*
|
|
1067
1070
|
* @param {String} key ONYXKEYS key
|
|
1068
|
-
* @param {(Object|Array)}
|
|
1071
|
+
* @param {(Object|Array)} changes Object or Array value to merge
|
|
1069
1072
|
* @returns {Promise}
|
|
1070
1073
|
*/
|
|
1071
|
-
function merge(key,
|
|
1074
|
+
function merge(key, changes) {
|
|
1075
|
+
// Merge attempts are batched together. The delta should be applied after a single call to get() to prevent a race condition.
|
|
1076
|
+
// Using the initial value from storage in subsequent merge attempts will lead to an incorrect final merged value.
|
|
1072
1077
|
if (mergeQueue[key]) {
|
|
1073
|
-
mergeQueue[key].push(
|
|
1078
|
+
mergeQueue[key].push(changes);
|
|
1074
1079
|
return Promise.resolve();
|
|
1075
1080
|
}
|
|
1081
|
+
mergeQueue[key] = [changes];
|
|
1076
1082
|
|
|
1077
|
-
mergeQueue[key] = [value];
|
|
1078
1083
|
return get(key).
|
|
1079
|
-
then((
|
|
1084
|
+
then((existingValue) => {
|
|
1080
1085
|
try {
|
|
1081
|
-
|
|
1086
|
+
// We first only merge the changes, so we can provide these to the native implementation (SQLite uses only delta changes in "JSON_PATCH" to merge)
|
|
1087
|
+
const batchedChanges = applyMerge(undefined, mergeQueue[key]);
|
|
1082
1088
|
|
|
1083
1089
|
// Clean up the write queue so we
|
|
1084
1090
|
// don't apply these changes again
|
|
1085
1091
|
delete mergeQueue[key];
|
|
1086
1092
|
|
|
1087
|
-
|
|
1093
|
+
// After that we merge the batched changes with the existing value
|
|
1094
|
+
let modifiedData = applyMerge(existingValue, [batchedChanges]);
|
|
1095
|
+
|
|
1096
|
+
// For objects, the key for null values needs to be removed from the object to ensure the value will get removed from storage completely.
|
|
1097
|
+
// On native, SQLite will remove top-level keys that are null. To be consistent, we remove them on web too.
|
|
1098
|
+
if (!underscore__WEBPACK_IMPORTED_MODULE_2___default().isArray(modifiedData) && underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject(modifiedData)) {
|
|
1099
|
+
modifiedData = underscore__WEBPACK_IMPORTED_MODULE_2___default().omit(modifiedData, (value) => underscore__WEBPACK_IMPORTED_MODULE_2___default().isNull(value));
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
const hasChanged = _OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].hasValueChanged(key, modifiedData);
|
|
1103
|
+
|
|
1104
|
+
// This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
|
|
1105
|
+
broadcastUpdate(key, modifiedData, hasChanged, 'merge');
|
|
1106
|
+
|
|
1107
|
+
// If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
|
|
1108
|
+
if (!hasChanged) {
|
|
1109
|
+
return Promise.resolve();
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
return _storage__WEBPACK_IMPORTED_MODULE_5__["default"].mergeItem(key, batchedChanges, modifiedData);
|
|
1088
1113
|
} catch (error) {
|
|
1089
1114
|
_Logger__WEBPACK_IMPORTED_MODULE_6__.logAlert(`An error occurred while applying merge for key: ${key}, Error: ${error}`);
|
|
1090
1115
|
}
|
|
@@ -1401,6 +1426,7 @@ const Onyx = {
|
|
|
1401
1426
|
multiSet,
|
|
1402
1427
|
merge,
|
|
1403
1428
|
mergeCollection,
|
|
1429
|
+
hasPendingMergeForKey,
|
|
1404
1430
|
update,
|
|
1405
1431
|
clear,
|
|
1406
1432
|
getAllKeys,
|
|
@@ -2208,6 +2234,16 @@ const provider = {
|
|
|
2208
2234
|
return localforage__WEBPACK_IMPORTED_MODULE_0___default().setItem(key, value);
|
|
2209
2235
|
}),
|
|
2210
2236
|
|
|
2237
|
+
/**
|
|
2238
|
+
* Sets the value for a given key. The only requirement is that the value should be serializable to JSON string
|
|
2239
|
+
* @param {String} key
|
|
2240
|
+
* @param {*} value
|
|
2241
|
+
* @return {Promise<void>}
|
|
2242
|
+
*/
|
|
2243
|
+
setItem(key, value) {
|
|
2244
|
+
return this.setItemQueue.push({ key, value });
|
|
2245
|
+
},
|
|
2246
|
+
|
|
2211
2247
|
/**
|
|
2212
2248
|
* Get multiple key-value pairs for the give array of keys in a batch
|
|
2213
2249
|
* @param {String[]} keys
|
|
@@ -2235,6 +2271,17 @@ const provider = {
|
|
|
2235
2271
|
return Promise.all(tasks).then(() => Promise.resolve());
|
|
2236
2272
|
},
|
|
2237
2273
|
|
|
2274
|
+
/**
|
|
2275
|
+
* Merging an existing value with a new one
|
|
2276
|
+
* @param {String} key
|
|
2277
|
+
* @param {any} _changes - not used, as we rely on the pre-merged data from the `modifiedData`
|
|
2278
|
+
* @param {any} modifiedData - the pre-merged data from `Onyx.applyMerge`
|
|
2279
|
+
* @return {Promise<void>}
|
|
2280
|
+
*/
|
|
2281
|
+
mergeItem(key, _changes, modifiedData) {
|
|
2282
|
+
return this.setItem(key, modifiedData);
|
|
2283
|
+
},
|
|
2284
|
+
|
|
2238
2285
|
/**
|
|
2239
2286
|
* Stores multiple key-value pairs in a batch
|
|
2240
2287
|
* @param {Array<[key, value]>} pairs
|
|
@@ -2285,16 +2332,6 @@ const provider = {
|
|
|
2285
2332
|
return localforage__WEBPACK_IMPORTED_MODULE_0___default().removeItems(keys);
|
|
2286
2333
|
},
|
|
2287
2334
|
|
|
2288
|
-
/**
|
|
2289
|
-
* Sets the value for a given key. The only requirement is that the value should be serializable to JSON string
|
|
2290
|
-
* @param {String} key
|
|
2291
|
-
* @param {*} value
|
|
2292
|
-
* @return {Promise<void>}
|
|
2293
|
-
*/
|
|
2294
|
-
setItem(key, value) {
|
|
2295
|
-
return this.setItemQueue.push({ key, value });
|
|
2296
|
-
},
|
|
2297
|
-
|
|
2298
2335
|
/**
|
|
2299
2336
|
* @param {string[]} keyList
|
|
2300
2337
|
*/
|