diff --git a/lib/Onyx.js b/lib/Onyx.js index 367f28b2..36c67e4f 100644 --- a/lib/Onyx.js +++ b/lib/Onyx.js @@ -1021,17 +1021,18 @@ function evictStorageAndRetry(error, onyxMethod, ...args) { * * @param {String} key * @param {*} value - * @param {Boolean} hasChanged * @param {String} method + * @param {Boolean} hasChanged + * @param {Boolean} wasRemoved * @returns {Promise} */ -function broadcastUpdate(key, value, hasChanged, method) { +function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) { // Logging properties only since values could be sensitive things we don't want to log Logger.logInfo(`${method}() called for key: ${key}${_.isObject(value) ? ` properties: ${_.keys(value).join(',')}` : ''}`); // Update subscribers if the cached value has changed, or when the subscriber specifically requires // all updates regardless of value changes (indicated by initWithStoredValues set to false). - if (hasChanged) { + if (hasChanged && !wasRemoved) { cache.set(key, value); } else { cache.addToAccessedKeys(key); @@ -1053,20 +1054,21 @@ function hasPendingMergeForKey(key) { * Otherwise removes all nested null values in objects and returns the object * @param {String} key * @param {Mixed} value - * @returns {Mixed} `null` if the key got removed completely, otherwise the value without null values + * @returns {Mixed} The value without null values and a boolean "wasRemoved", which indicates if the key got removed completely */ function removeNullValues(key, value) { if (_.isNull(value)) { remove(key); - return null; + return {value, wasRemoved: true}; } // We can remove all null values in an object by merging it with itself // utils.fastMerge recursively goes through the object and removes all null values // Passing two identical objects as source and target to fastMerge will not change it, but only remove the null values - return utils.removeNestedNullValues(value); + return {value: utils.removeNestedNullValues(value), wasRemoved: false}; } +const setOperationPromise = {} /** * Write a value to our store with the given key * @@ -1085,28 +1087,33 @@ function set(key, value) { return Promise.resolve(); } - const valueWithoutNull = removeNullValues(key, value); + // If the value is null, we remove the key from storage + const {value: valueAfterRemoving, wasRemoved} = removeNullValues(key, value); - if (valueWithoutNull === null) { - return Promise.resolve(); - } + console.log({queue: mergeQueue[key]}) if (hasPendingMergeForKey(key)) { + console.log("merge ongoing") Logger.logAlert(`Onyx.set() called after Onyx.merge() for key: ${key}. It is recommended to use set() or merge() not both.`); } - const hasChanged = cache.hasValueChanged(key, valueWithoutNull); + const hasChanged = cache.hasValueChanged(key, valueAfterRemoving); // This approach prioritizes fast UI changes without waiting for data to be stored in device storage. - const updatePromise = broadcastUpdate(key, valueWithoutNull, hasChanged, 'set'); + const updatePromise = broadcastUpdate(key, valueAfterRemoving, 'set', hasChanged, wasRemoved); + + // If the key got removed earlier, we don't have to set the value in storage, so return early instead + if (wasRemoved) { + return updatePromise; + } // If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead. if (!hasChanged) { return updatePromise; } - return Storage.setItem(key, valueWithoutNull) - .catch((error) => evictStorageAndRetry(error, set, key, valueWithoutNull)) + return Storage.setItem(key, valueAfterRemoving) + .catch((error) => evictStorageAndRetry(error, set, key, valueAfterRemoving)) .then(() => updatePromise); } @@ -1116,10 +1123,10 @@ function set(key, value) { * to an array of key-value pairs in the above format * @private * @param {Record} data - * @return {Array} an array of key - value pairs <[key, value]> + * @return {Array} an array of key - value pairs and a boolean which indicates if the key got removed <[key, {value, wasRemoved}]> */ function prepareKeyValuePairsForStorage(data) { - return _.map(data, (value, key) => [key, value]); + return _.map(data, (value, key) => [key, removeNullValues(value)]); } /** @@ -1142,20 +1149,18 @@ function multiSet(data) { const keyValuePairs = prepareKeyValuePairsForStorage(data); - const updatePromises = _.map(data, (val, key) => { + const updatePromises = _.map(keyValuePairs, ([key, {value, wasRemoved}]) => { // Update cache and optimistically inform subscribers on the next tick - cache.set(key, val); - return scheduleSubscriberUpdate(key, val); + cache.set(key, value); + return scheduleSubscriberUpdate(key, value); }); const keyValuePairsWithoutNull = _.filter( - _.map(keyValuePairs, ([key, value]) => { - const valueWithoutNull = removeNullValues(key, value); - - if (valueWithoutNull === null) { + _.map(keyValuePairs, ([key, {value, wasRemoved}]) => { + if (wasRemoved) { return; } - return [key, valueWithoutNull]; + return [key, value]; }), Boolean, );