react-native-onyx 1.0.131 → 2.0.2
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/API.md +83 -22
- package/README.md +3 -5
- package/dist/DevTools.d.ts +23 -0
- package/{lib → dist}/DevTools.js +16 -18
- package/dist/Logger.js +32 -0
- package/dist/MDTable.d.ts +36 -0
- package/{lib → dist}/MDTable.js +12 -17
- package/{lib → dist}/Onyx.js +278 -477
- package/dist/OnyxCache.d.ts +121 -0
- package/{lib → dist}/OnyxCache.js +16 -53
- package/{lib/Str.js → dist/Str.d.ts} +2 -11
- package/dist/Str.js +31 -0
- package/dist/SyncQueue.d.ts +32 -0
- package/{lib → dist}/SyncQueue.js +9 -11
- package/dist/batch.d.ts +2 -0
- package/dist/batch.js +4 -0
- package/dist/batch.native.d.ts +2 -0
- package/dist/batch.native.js +4 -0
- package/dist/compose.d.ts +19 -0
- package/{lib → dist}/compose.js +5 -8
- package/dist/createDeferredTask.d.ts +12 -0
- package/{lib → dist}/createDeferredTask.js +4 -2
- package/dist/index.d.ts +6 -0
- package/dist/index.js +10 -0
- package/dist/metrics/PerformanceUtils.d.ts +14 -0
- package/{lib → dist}/metrics/PerformanceUtils.js +16 -16
- package/dist/metrics/index.d.ts +4 -0
- package/dist/metrics/index.js +14 -0
- package/dist/metrics/index.native.d.ts +43 -0
- package/{lib → dist}/metrics/index.native.js +80 -102
- package/{lib/storage/NativeStorage.js → dist/storage/NativeStorage.d.ts} +1 -2
- package/dist/storage/NativeStorage.js +7 -0
- package/dist/storage/WebStorage.d.ts +19 -0
- package/{lib → dist}/storage/WebStorage.js +24 -34
- package/dist/storage/__mocks__/index.d.ts +23 -0
- package/{lib → dist}/storage/__mocks__/index.js +17 -19
- package/{lib/storage/index.web.js → dist/storage/index.d.ts} +1 -2
- package/dist/storage/index.js +7 -0
- package/{lib/storage/index.native.js → dist/storage/index.native.d.ts} +1 -2
- package/dist/storage/index.native.js +7 -0
- package/dist/storage/providers/IDBKeyVal.d.ts +26 -0
- package/{lib → dist}/storage/providers/IDBKeyVal.js +38 -52
- package/dist/storage/providers/SQLiteStorage.d.ts +52 -0
- package/{lib → dist}/storage/providers/SQLiteStorage.js +27 -42
- package/{lib → dist}/utils.js +14 -27
- package/{lib → dist}/withOnyx.js +122 -159
- package/package.json +23 -54
- package/dist/web.development.js +0 -4308
- package/dist/web.development.js.map +0 -1
- package/dist/web.min.js +0 -2
- package/dist/web.min.js.map +0 -1
- package/lib/Logger.js +0 -31
- package/lib/batch.js +0 -3
- package/lib/batch.native.js +0 -3
- package/lib/index.d.ts +0 -6
- package/lib/index.js +0 -5
- package/lib/metrics/index.web.js +0 -10
- package/native.js +0 -11
- package/web.js +0 -12
- /package/{lib → dist}/Logger.d.ts +0 -0
- /package/{lib → dist}/Onyx.d.ts +0 -0
- /package/{lib → dist}/types.d.ts +0 -0
- /package/{lib → dist}/utils.d.ts +0 -0
- /package/{lib → dist}/withOnyx.d.ts +0 -0
package/{lib → dist}/Onyx.js
RENAMED
|
@@ -1,16 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
29
|
/* eslint-disable no-continue */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
30
|
+
const fast_equals_1 = require("fast-equals");
|
|
31
|
+
const underscore_1 = __importDefault(require("underscore"));
|
|
32
|
+
const Logger = __importStar(require("./Logger"));
|
|
33
|
+
const OnyxCache_1 = __importDefault(require("./OnyxCache"));
|
|
34
|
+
const Str = __importStar(require("./Str"));
|
|
35
|
+
const createDeferredTask_1 = __importDefault(require("./createDeferredTask"));
|
|
36
|
+
const PerformanceUtils = __importStar(require("./metrics/PerformanceUtils"));
|
|
37
|
+
const storage_1 = __importDefault(require("./storage"));
|
|
38
|
+
const utils_1 = __importDefault(require("./utils"));
|
|
39
|
+
const batch_1 = __importDefault(require("./batch"));
|
|
40
|
+
const DevTools_1 = __importDefault(require("./DevTools"));
|
|
14
41
|
// Method constants
|
|
15
42
|
const METHOD = {
|
|
16
43
|
SET: 'set',
|
|
@@ -19,40 +46,29 @@ const METHOD = {
|
|
|
19
46
|
MULTI_SET: 'multiset',
|
|
20
47
|
CLEAR: 'clear',
|
|
21
48
|
};
|
|
22
|
-
|
|
23
49
|
// Key/value store of Onyx key and arrays of values to merge
|
|
24
50
|
const mergeQueue = {};
|
|
25
51
|
const mergeQueuePromise = {};
|
|
26
|
-
|
|
27
52
|
// Keeps track of the last connectionID that was used so we can keep incrementing it
|
|
28
53
|
let lastConnectionID = 0;
|
|
29
|
-
|
|
30
54
|
// Holds a mapping of all the react components that want their state subscribed to a store key
|
|
31
55
|
const callbackToStateMapping = {};
|
|
32
|
-
|
|
33
56
|
// Keeps a copy of the values of the onyx collection keys as a map for faster lookups
|
|
34
57
|
let onyxCollectionKeyMap = new Map();
|
|
35
|
-
|
|
36
58
|
// Holds a list of keys that have been directly subscribed to or recently modified from least to most recent
|
|
37
59
|
let recentlyAccessedKeys = [];
|
|
38
|
-
|
|
39
60
|
// Holds a list of keys that are safe to remove when we reach max storage. If a key does not match with
|
|
40
61
|
// whatever appears in this list it will NEVER be a candidate for eviction.
|
|
41
62
|
let evictionAllowList = [];
|
|
42
|
-
|
|
43
63
|
// Holds a map of keys and connectionID arrays whose keys will never be automatically evicted as
|
|
44
64
|
// long as we have at least one subscriber that returns false for the canEvict property.
|
|
45
65
|
const evictionBlocklist = {};
|
|
46
|
-
|
|
47
66
|
// Optional user-provided key value states set when Onyx initializes or clears
|
|
48
67
|
let defaultKeyStates = {};
|
|
49
|
-
|
|
50
68
|
// Connections can be made before `Onyx.init`. They would wait for this task before resolving
|
|
51
|
-
const deferredInitTask =
|
|
52
|
-
|
|
69
|
+
const deferredInitTask = (0, createDeferredTask_1.default)();
|
|
53
70
|
let batchUpdatesPromise = null;
|
|
54
71
|
let batchUpdatesQueue = [];
|
|
55
|
-
|
|
56
72
|
/**
|
|
57
73
|
* Sends an action to DevTools extension
|
|
58
74
|
*
|
|
@@ -62,9 +78,8 @@ let batchUpdatesQueue = [];
|
|
|
62
78
|
* @param {any} mergedValue - (optional) value that was written in the storage after a merge method was executed.
|
|
63
79
|
*/
|
|
64
80
|
function sendActionToDevTools(method, key, value, mergedValue = undefined) {
|
|
65
|
-
|
|
81
|
+
DevTools_1.default.registerAction(utils_1.default.formatActionName(method, key), value, key ? { [key]: mergedValue || value } : value);
|
|
66
82
|
}
|
|
67
|
-
|
|
68
83
|
/**
|
|
69
84
|
* We are batching together onyx updates. This helps with use cases where we schedule onyx updates after each other.
|
|
70
85
|
* This happens for example in the Onyx.update function, where we process API responses that might contain a lot of
|
|
@@ -76,7 +91,6 @@ function maybeFlushBatchUpdates() {
|
|
|
76
91
|
if (batchUpdatesPromise) {
|
|
77
92
|
return batchUpdatesPromise;
|
|
78
93
|
}
|
|
79
|
-
|
|
80
94
|
batchUpdatesPromise = new Promise((resolve) => {
|
|
81
95
|
/* We use (setTimeout, 0) here which should be called once native module calls are flushed (usually at the end of the frame)
|
|
82
96
|
* We may investigate if (setTimeout, 1) (which in React Native is equal to requestAnimationFrame) works even better
|
|
@@ -86,23 +100,20 @@ function maybeFlushBatchUpdates() {
|
|
|
86
100
|
const updatesCopy = batchUpdatesQueue;
|
|
87
101
|
batchUpdatesQueue = [];
|
|
88
102
|
batchUpdatesPromise = null;
|
|
89
|
-
|
|
103
|
+
(0, batch_1.default)(() => {
|
|
90
104
|
updatesCopy.forEach((applyUpdates) => {
|
|
91
105
|
applyUpdates();
|
|
92
106
|
});
|
|
93
107
|
});
|
|
94
|
-
|
|
95
108
|
resolve();
|
|
96
109
|
}, 0);
|
|
97
110
|
});
|
|
98
111
|
return batchUpdatesPromise;
|
|
99
112
|
}
|
|
100
|
-
|
|
101
113
|
function batchUpdates(updates) {
|
|
102
114
|
batchUpdatesQueue.push(updates);
|
|
103
115
|
return maybeFlushBatchUpdates();
|
|
104
116
|
}
|
|
105
|
-
|
|
106
117
|
/**
|
|
107
118
|
* Uses a selector function to return a simplified version of sourceData
|
|
108
119
|
* @param {Mixed} sourceData
|
|
@@ -111,7 +122,6 @@ function batchUpdates(updates) {
|
|
|
111
122
|
* @returns {Mixed}
|
|
112
123
|
*/
|
|
113
124
|
const getSubsetOfData = (sourceData, selector, withOnyxInstanceState) => selector(sourceData, withOnyxInstanceState);
|
|
114
|
-
|
|
115
125
|
/**
|
|
116
126
|
* Takes a collection of items (eg. {testKey_1:{a:'a'}, testKey_2:{b:'b'}})
|
|
117
127
|
* and runs it through a reducer function to return a subset of the data according to a selector.
|
|
@@ -121,18 +131,11 @@ const getSubsetOfData = (sourceData, selector, withOnyxInstanceState) => selecto
|
|
|
121
131
|
* @param {Object} [withOnyxInstanceState]
|
|
122
132
|
* @returns {Object}
|
|
123
133
|
*/
|
|
124
|
-
const reduceCollectionWithSelector = (collection, selector, withOnyxInstanceState) =>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
finalCollection[key] = getSubsetOfData(item, selector, withOnyxInstanceState);
|
|
130
|
-
|
|
131
|
-
return finalCollection;
|
|
132
|
-
},
|
|
133
|
-
{},
|
|
134
|
-
);
|
|
135
|
-
|
|
134
|
+
const reduceCollectionWithSelector = (collection, selector, withOnyxInstanceState) => underscore_1.default.reduce(collection, (finalCollection, item, key) => {
|
|
135
|
+
// eslint-disable-next-line no-param-reassign
|
|
136
|
+
finalCollection[key] = getSubsetOfData(item, selector, withOnyxInstanceState);
|
|
137
|
+
return finalCollection;
|
|
138
|
+
}, {});
|
|
136
139
|
/**
|
|
137
140
|
* Get some data from the store
|
|
138
141
|
*
|
|
@@ -142,28 +145,23 @@ const reduceCollectionWithSelector = (collection, selector, withOnyxInstanceStat
|
|
|
142
145
|
*/
|
|
143
146
|
function get(key) {
|
|
144
147
|
// When we already have the value in cache - resolve right away
|
|
145
|
-
if (
|
|
146
|
-
return Promise.resolve(
|
|
148
|
+
if (OnyxCache_1.default.hasCacheForKey(key)) {
|
|
149
|
+
return Promise.resolve(OnyxCache_1.default.getValue(key));
|
|
147
150
|
}
|
|
148
|
-
|
|
149
151
|
const taskName = `get:${key}`;
|
|
150
|
-
|
|
151
152
|
// When a value retrieving task for this key is still running hook to it
|
|
152
|
-
if (
|
|
153
|
-
return
|
|
153
|
+
if (OnyxCache_1.default.hasPendingTask(taskName)) {
|
|
154
|
+
return OnyxCache_1.default.getTaskPromise(taskName);
|
|
154
155
|
}
|
|
155
|
-
|
|
156
156
|
// Otherwise retrieve the value from storage and capture a promise to aid concurrent usages
|
|
157
|
-
const promise =
|
|
157
|
+
const promise = storage_1.default.getItem(key)
|
|
158
158
|
.then((val) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
OnyxCache_1.default.set(key, val);
|
|
160
|
+
return val;
|
|
161
|
+
})
|
|
162
162
|
.catch((err) => Logger.logInfo(`Unable to get item from persistent storage. Key: ${key} Error: ${err}`));
|
|
163
|
-
|
|
164
|
-
return cache.captureTask(taskName, promise);
|
|
163
|
+
return OnyxCache_1.default.captureTask(taskName, promise);
|
|
165
164
|
}
|
|
166
|
-
|
|
167
165
|
/**
|
|
168
166
|
* Returns current key names stored in persisted storage
|
|
169
167
|
* @private
|
|
@@ -171,27 +169,22 @@ function get(key) {
|
|
|
171
169
|
*/
|
|
172
170
|
function getAllKeys() {
|
|
173
171
|
// When we've already read stored keys, resolve right away
|
|
174
|
-
const storedKeys =
|
|
172
|
+
const storedKeys = OnyxCache_1.default.getAllKeys();
|
|
175
173
|
if (storedKeys.length > 0) {
|
|
176
174
|
return Promise.resolve(storedKeys);
|
|
177
175
|
}
|
|
178
|
-
|
|
179
176
|
const taskName = 'getAllKeys';
|
|
180
|
-
|
|
181
177
|
// When a value retrieving task for all keys is still running hook to it
|
|
182
|
-
if (
|
|
183
|
-
return
|
|
178
|
+
if (OnyxCache_1.default.hasPendingTask(taskName)) {
|
|
179
|
+
return OnyxCache_1.default.getTaskPromise(taskName);
|
|
184
180
|
}
|
|
185
|
-
|
|
186
181
|
// Otherwise retrieve the keys from storage and capture a promise to aid concurrent usages
|
|
187
|
-
const promise =
|
|
188
|
-
|
|
182
|
+
const promise = storage_1.default.getAllKeys().then((keys) => {
|
|
183
|
+
underscore_1.default.each(keys, (key) => OnyxCache_1.default.addKey(key));
|
|
189
184
|
return keys;
|
|
190
185
|
});
|
|
191
|
-
|
|
192
|
-
return cache.captureTask(taskName, promise);
|
|
186
|
+
return OnyxCache_1.default.captureTask(taskName, promise);
|
|
193
187
|
}
|
|
194
|
-
|
|
195
188
|
/**
|
|
196
189
|
* Checks to see if the a subscriber's supplied key
|
|
197
190
|
* is associated with a collection of keys.
|
|
@@ -203,7 +196,6 @@ function getAllKeys() {
|
|
|
203
196
|
function isCollectionKey(key) {
|
|
204
197
|
return onyxCollectionKeyMap.has(key);
|
|
205
198
|
}
|
|
206
|
-
|
|
207
199
|
/**
|
|
208
200
|
* @param {String} collectionKey
|
|
209
201
|
* @param {String} key
|
|
@@ -212,7 +204,6 @@ function isCollectionKey(key) {
|
|
|
212
204
|
function isCollectionMemberKey(collectionKey, key) {
|
|
213
205
|
return Str.startsWith(key, collectionKey) && key.length > collectionKey.length;
|
|
214
206
|
}
|
|
215
|
-
|
|
216
207
|
/**
|
|
217
208
|
* Checks to see if a provided key is the exact configured key of our connected subscriber
|
|
218
209
|
* or if the provided key is a collection member key (in case our configured key is a "collection key")
|
|
@@ -225,7 +216,6 @@ function isCollectionMemberKey(collectionKey, key) {
|
|
|
225
216
|
function isKeyMatch(configKey, key) {
|
|
226
217
|
return isCollectionKey(configKey) ? Str.startsWith(key, configKey) : configKey === key;
|
|
227
218
|
}
|
|
228
|
-
|
|
229
219
|
/**
|
|
230
220
|
* Checks to see if this key has been flagged as
|
|
231
221
|
* safe for removal.
|
|
@@ -235,9 +225,8 @@ function isKeyMatch(configKey, key) {
|
|
|
235
225
|
* @returns {Boolean}
|
|
236
226
|
*/
|
|
237
227
|
function isSafeEvictionKey(testKey) {
|
|
238
|
-
return
|
|
228
|
+
return underscore_1.default.some(evictionAllowList, (key) => isKeyMatch(key, testKey));
|
|
239
229
|
}
|
|
240
|
-
|
|
241
230
|
/**
|
|
242
231
|
* Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
|
|
243
232
|
* If the requested key is a collection, it will return an object with all the collection members.
|
|
@@ -247,34 +236,26 @@ function isSafeEvictionKey(testKey) {
|
|
|
247
236
|
* @returns {Mixed}
|
|
248
237
|
*/
|
|
249
238
|
function tryGetCachedValue(key, mapping = {}) {
|
|
250
|
-
let val =
|
|
251
|
-
|
|
239
|
+
let val = OnyxCache_1.default.getValue(key);
|
|
252
240
|
if (isCollectionKey(key)) {
|
|
253
|
-
const allCacheKeys =
|
|
254
|
-
|
|
241
|
+
const allCacheKeys = OnyxCache_1.default.getAllKeys();
|
|
255
242
|
// It is possible we haven't loaded all keys yet so we do not know if the
|
|
256
243
|
// collection actually exists.
|
|
257
244
|
if (allCacheKeys.length === 0) {
|
|
258
245
|
return;
|
|
259
246
|
}
|
|
260
|
-
const matchingKeys =
|
|
261
|
-
const values =
|
|
262
|
-
|
|
263
|
-
(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return finalObject;
|
|
271
|
-
},
|
|
272
|
-
{},
|
|
273
|
-
);
|
|
274
|
-
|
|
247
|
+
const matchingKeys = underscore_1.default.filter(allCacheKeys, (k) => k.startsWith(key));
|
|
248
|
+
const values = underscore_1.default.reduce(matchingKeys, (finalObject, matchedKey) => {
|
|
249
|
+
const cachedValue = OnyxCache_1.default.getValue(matchedKey);
|
|
250
|
+
if (cachedValue) {
|
|
251
|
+
// This is permissible because we're in the process of constructing the final object in a reduce function.
|
|
252
|
+
// eslint-disable-next-line no-param-reassign
|
|
253
|
+
finalObject[matchedKey] = cachedValue;
|
|
254
|
+
}
|
|
255
|
+
return finalObject;
|
|
256
|
+
}, {});
|
|
275
257
|
val = values;
|
|
276
258
|
}
|
|
277
|
-
|
|
278
259
|
if (mapping.selector) {
|
|
279
260
|
const state = mapping.withOnyxInstance ? mapping.withOnyxInstance.state : undefined;
|
|
280
261
|
if (isCollectionKey(key)) {
|
|
@@ -282,10 +263,8 @@ function tryGetCachedValue(key, mapping = {}) {
|
|
|
282
263
|
}
|
|
283
264
|
return getSubsetOfData(val, mapping.selector, state);
|
|
284
265
|
}
|
|
285
|
-
|
|
286
266
|
return val;
|
|
287
267
|
}
|
|
288
|
-
|
|
289
268
|
/**
|
|
290
269
|
* Remove a key from the recently accessed key list.
|
|
291
270
|
*
|
|
@@ -293,9 +272,8 @@ function tryGetCachedValue(key, mapping = {}) {
|
|
|
293
272
|
* @param {String} key
|
|
294
273
|
*/
|
|
295
274
|
function removeLastAccessedKey(key) {
|
|
296
|
-
recentlyAccessedKeys =
|
|
275
|
+
recentlyAccessedKeys = underscore_1.default.without(recentlyAccessedKeys, key);
|
|
297
276
|
}
|
|
298
|
-
|
|
299
277
|
/**
|
|
300
278
|
* Add a key to the list of recently accessed keys. The least
|
|
301
279
|
* recently accessed key should be at the head and the most
|
|
@@ -309,11 +287,9 @@ function addLastAccessedKey(key) {
|
|
|
309
287
|
if (isCollectionKey(key) || !isSafeEvictionKey(key)) {
|
|
310
288
|
return;
|
|
311
289
|
}
|
|
312
|
-
|
|
313
290
|
removeLastAccessedKey(key);
|
|
314
291
|
recentlyAccessedKeys.push(key);
|
|
315
292
|
}
|
|
316
|
-
|
|
317
293
|
/**
|
|
318
294
|
* Removes a key previously added to this list
|
|
319
295
|
* which will enable it to be deleted again.
|
|
@@ -323,14 +299,12 @@ function addLastAccessedKey(key) {
|
|
|
323
299
|
* @param {Number} connectionID
|
|
324
300
|
*/
|
|
325
301
|
function removeFromEvictionBlockList(key, connectionID) {
|
|
326
|
-
evictionBlocklist[key] =
|
|
327
|
-
|
|
302
|
+
evictionBlocklist[key] = underscore_1.default.without(evictionBlocklist[key] || [], connectionID);
|
|
328
303
|
// Remove the key if there are no more subscribers
|
|
329
304
|
if (evictionBlocklist[key].length === 0) {
|
|
330
305
|
delete evictionBlocklist[key];
|
|
331
306
|
}
|
|
332
307
|
}
|
|
333
|
-
|
|
334
308
|
/**
|
|
335
309
|
* Keys added to this list can never be deleted.
|
|
336
310
|
*
|
|
@@ -340,14 +314,11 @@ function removeFromEvictionBlockList(key, connectionID) {
|
|
|
340
314
|
*/
|
|
341
315
|
function addToEvictionBlockList(key, connectionID) {
|
|
342
316
|
removeFromEvictionBlockList(key, connectionID);
|
|
343
|
-
|
|
344
317
|
if (!evictionBlocklist[key]) {
|
|
345
318
|
evictionBlocklist[key] = [];
|
|
346
319
|
}
|
|
347
|
-
|
|
348
320
|
evictionBlocklist[key].push(connectionID);
|
|
349
321
|
}
|
|
350
|
-
|
|
351
322
|
/**
|
|
352
323
|
* Take all the keys that are safe to evict and add them to
|
|
353
324
|
* the recently accessed list when initializing the app. This
|
|
@@ -359,8 +330,8 @@ function addToEvictionBlockList(key, connectionID) {
|
|
|
359
330
|
*/
|
|
360
331
|
function addAllSafeEvictionKeysToRecentlyAccessedList() {
|
|
361
332
|
return getAllKeys().then((keys) => {
|
|
362
|
-
|
|
363
|
-
|
|
333
|
+
underscore_1.default.each(evictionAllowList, (safeEvictionKey) => {
|
|
334
|
+
underscore_1.default.each(keys, (key) => {
|
|
364
335
|
if (!isKeyMatch(safeEvictionKey, key)) {
|
|
365
336
|
return;
|
|
366
337
|
}
|
|
@@ -369,31 +340,23 @@ function addAllSafeEvictionKeysToRecentlyAccessedList() {
|
|
|
369
340
|
});
|
|
370
341
|
});
|
|
371
342
|
}
|
|
372
|
-
|
|
373
343
|
/**
|
|
374
344
|
* @private
|
|
375
345
|
* @param {String} collectionKey
|
|
376
346
|
* @returns {Object}
|
|
377
347
|
*/
|
|
378
348
|
function getCachedCollection(collectionKey) {
|
|
379
|
-
const collectionMemberKeys =
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
(prev, curr) => {
|
|
384
|
-
const cachedValue = cache.getValue(curr);
|
|
385
|
-
if (!cachedValue) {
|
|
386
|
-
return prev;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// eslint-disable-next-line no-param-reassign
|
|
390
|
-
prev[curr] = cachedValue;
|
|
349
|
+
const collectionMemberKeys = underscore_1.default.filter(OnyxCache_1.default.getAllKeys(), (storedKey) => isCollectionMemberKey(collectionKey, storedKey));
|
|
350
|
+
return underscore_1.default.reduce(collectionMemberKeys, (prev, curr) => {
|
|
351
|
+
const cachedValue = OnyxCache_1.default.getValue(curr);
|
|
352
|
+
if (!cachedValue) {
|
|
391
353
|
return prev;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
354
|
+
}
|
|
355
|
+
// eslint-disable-next-line no-param-reassign
|
|
356
|
+
prev[curr] = cachedValue;
|
|
357
|
+
return prev;
|
|
358
|
+
}, {});
|
|
395
359
|
}
|
|
396
|
-
|
|
397
360
|
/**
|
|
398
361
|
* When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
|
|
399
362
|
*
|
|
@@ -407,38 +370,32 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
407
370
|
// We are iterating over all subscribers similar to keyChanged(). However, we are looking for subscribers who are subscribing to either a collection key or
|
|
408
371
|
// individual collection key member for the collection that is being updated. It is important to note that the collection parameter cane be a PARTIAL collection
|
|
409
372
|
// and does not represent all of the combined keys and values for a collection key. It is just the "new" data that was merged in via mergeCollection().
|
|
410
|
-
const stateMappingKeys =
|
|
373
|
+
const stateMappingKeys = underscore_1.default.keys(callbackToStateMapping);
|
|
411
374
|
for (let i = 0; i < stateMappingKeys.length; i++) {
|
|
412
375
|
const subscriber = callbackToStateMapping[stateMappingKeys[i]];
|
|
413
376
|
if (!subscriber) {
|
|
414
377
|
continue;
|
|
415
378
|
}
|
|
416
|
-
|
|
417
379
|
// Skip iteration if we do not have a collection key or a collection member key on this subscriber
|
|
418
380
|
if (!Str.startsWith(subscriber.key, collectionKey)) {
|
|
419
381
|
continue;
|
|
420
382
|
}
|
|
421
|
-
|
|
422
383
|
/**
|
|
423
384
|
* e.g. Onyx.connect({key: ONYXKEYS.COLLECTION.REPORT, callback: ...});
|
|
424
385
|
*/
|
|
425
386
|
const isSubscribedToCollectionKey = subscriber.key === collectionKey;
|
|
426
|
-
|
|
427
387
|
/**
|
|
428
388
|
* e.g. Onyx.connect({key: `${ONYXKEYS.COLLECTION.REPORT}{reportID}`, callback: ...});
|
|
429
389
|
*/
|
|
430
390
|
const isSubscribedToCollectionMemberKey = isCollectionMemberKey(collectionKey, subscriber.key);
|
|
431
|
-
|
|
432
391
|
// We prepare the "cached collection" which is the entire collection + the new partial data that
|
|
433
392
|
// was merged in via mergeCollection().
|
|
434
393
|
const cachedCollection = getCachedCollection(collectionKey);
|
|
435
|
-
|
|
436
394
|
// Regular Onyx.connect() subscriber found.
|
|
437
|
-
if (
|
|
395
|
+
if (underscore_1.default.isFunction(subscriber.callback)) {
|
|
438
396
|
if (!notifyRegularSubscibers) {
|
|
439
397
|
continue;
|
|
440
398
|
}
|
|
441
|
-
|
|
442
399
|
// If they are subscribed to the collection key and using waitForCollectionCallback then we'll
|
|
443
400
|
// send the whole cached collection.
|
|
444
401
|
if (isSubscribedToCollectionKey) {
|
|
@@ -446,33 +403,28 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
446
403
|
subscriber.callback(cachedCollection);
|
|
447
404
|
continue;
|
|
448
405
|
}
|
|
449
|
-
|
|
450
406
|
// If they are not using waitForCollectionCallback then we notify the subscriber with
|
|
451
407
|
// the new merged data but only for any keys in the partial collection.
|
|
452
|
-
const dataKeys =
|
|
408
|
+
const dataKeys = underscore_1.default.keys(partialCollection);
|
|
453
409
|
for (let j = 0; j < dataKeys.length; j++) {
|
|
454
410
|
const dataKey = dataKeys[j];
|
|
455
411
|
subscriber.callback(cachedCollection[dataKey], dataKey);
|
|
456
412
|
}
|
|
457
413
|
continue;
|
|
458
414
|
}
|
|
459
|
-
|
|
460
415
|
// And if the subscriber is specifically only tracking a particular collection member key then we will
|
|
461
416
|
// notify them with the cached data for that key only.
|
|
462
417
|
if (isSubscribedToCollectionMemberKey) {
|
|
463
418
|
subscriber.callback(cachedCollection[subscriber.key], subscriber.key);
|
|
464
419
|
continue;
|
|
465
420
|
}
|
|
466
|
-
|
|
467
421
|
continue;
|
|
468
422
|
}
|
|
469
|
-
|
|
470
423
|
// React component subscriber found.
|
|
471
424
|
if (subscriber.withOnyxInstance) {
|
|
472
425
|
if (!notifyWithOnyxSubscibers) {
|
|
473
426
|
continue;
|
|
474
427
|
}
|
|
475
|
-
|
|
476
428
|
// We are subscribed to a collection key so we must update the data in state with the new
|
|
477
429
|
// collection member key values from the partial update.
|
|
478
430
|
if (isSubscribedToCollectionKey) {
|
|
@@ -482,8 +434,7 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
482
434
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
483
435
|
const previousData = prevState[subscriber.statePropertyName];
|
|
484
436
|
const newData = reduceCollectionWithSelector(cachedCollection, subscriber.selector, subscriber.withOnyxInstance.state);
|
|
485
|
-
|
|
486
|
-
if (!deepEqual(previousData, newData)) {
|
|
437
|
+
if (!(0, fast_equals_1.deepEqual)(previousData, newData)) {
|
|
487
438
|
return {
|
|
488
439
|
[subscriber.statePropertyName]: newData,
|
|
489
440
|
};
|
|
@@ -492,15 +443,13 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
492
443
|
});
|
|
493
444
|
continue;
|
|
494
445
|
}
|
|
495
|
-
|
|
496
446
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
497
|
-
const finalCollection =
|
|
498
|
-
const dataKeys =
|
|
447
|
+
const finalCollection = underscore_1.default.clone(prevState[subscriber.statePropertyName] || {});
|
|
448
|
+
const dataKeys = underscore_1.default.keys(partialCollection);
|
|
499
449
|
for (let j = 0; j < dataKeys.length; j++) {
|
|
500
450
|
const dataKey = dataKeys[j];
|
|
501
451
|
finalCollection[dataKey] = cachedCollection[dataKey];
|
|
502
452
|
}
|
|
503
|
-
|
|
504
453
|
PerformanceUtils.logSetStateCall(subscriber, prevState[subscriber.statePropertyName], finalCollection, 'keysChanged', collectionKey);
|
|
505
454
|
return {
|
|
506
455
|
[subscriber.statePropertyName]: finalCollection,
|
|
@@ -508,16 +457,14 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
508
457
|
});
|
|
509
458
|
continue;
|
|
510
459
|
}
|
|
511
|
-
|
|
512
460
|
// If a React component is only interested in a single key then we can set the cached value directly to the state name.
|
|
513
461
|
if (isSubscribedToCollectionMemberKey) {
|
|
514
462
|
// However, we only want to update this subscriber if the partial data contains a change.
|
|
515
463
|
// Otherwise, we would update them with a value they already have and trigger an unnecessary re-render.
|
|
516
464
|
const dataFromCollection = partialCollection[subscriber.key];
|
|
517
|
-
if (
|
|
465
|
+
if (underscore_1.default.isUndefined(dataFromCollection)) {
|
|
518
466
|
continue;
|
|
519
467
|
}
|
|
520
|
-
|
|
521
468
|
// If the subscriber has a selector, then the component's state must only be updated with the data
|
|
522
469
|
// returned by the selector and the state should only change when the subset of data changes from what
|
|
523
470
|
// it was previously.
|
|
@@ -525,30 +472,26 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
525
472
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
526
473
|
const prevData = prevState[subscriber.statePropertyName];
|
|
527
474
|
const newData = getSubsetOfData(cachedCollection[subscriber.key], subscriber.selector, subscriber.withOnyxInstance.state);
|
|
528
|
-
if (!deepEqual(prevData, newData)) {
|
|
475
|
+
if (!(0, fast_equals_1.deepEqual)(prevData, newData)) {
|
|
529
476
|
PerformanceUtils.logSetStateCall(subscriber, prevData, newData, 'keysChanged', collectionKey);
|
|
530
477
|
return {
|
|
531
478
|
[subscriber.statePropertyName]: newData,
|
|
532
479
|
};
|
|
533
480
|
}
|
|
534
|
-
|
|
535
481
|
return null;
|
|
536
482
|
});
|
|
537
483
|
continue;
|
|
538
484
|
}
|
|
539
|
-
|
|
540
485
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
541
486
|
const data = cachedCollection[subscriber.key];
|
|
542
487
|
const previousData = prevState[subscriber.statePropertyName];
|
|
543
|
-
|
|
544
488
|
// Avoids triggering unnecessary re-renders when feeding empty objects
|
|
545
|
-
if (
|
|
489
|
+
if (utils_1.default.areObjectsEmpty(data, previousData)) {
|
|
546
490
|
return null;
|
|
547
491
|
}
|
|
548
492
|
if (data === previousData) {
|
|
549
493
|
return null;
|
|
550
494
|
}
|
|
551
|
-
|
|
552
495
|
PerformanceUtils.logSetStateCall(subscriber, previousData, data, 'keysChanged', collectionKey);
|
|
553
496
|
return {
|
|
554
497
|
[subscriber.statePropertyName]: data,
|
|
@@ -558,7 +501,6 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
558
501
|
}
|
|
559
502
|
}
|
|
560
503
|
}
|
|
561
|
-
|
|
562
504
|
/**
|
|
563
505
|
* When a key change happens, search for any callbacks matching the key or collection key and trigger those callbacks
|
|
564
506
|
*
|
|
@@ -574,24 +516,23 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
574
516
|
*/
|
|
575
517
|
function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = true, notifyWithOnyxSubscibers = true) {
|
|
576
518
|
// Add or remove this key from the recentlyAccessedKeys lists
|
|
577
|
-
if (!
|
|
519
|
+
if (!underscore_1.default.isNull(data)) {
|
|
578
520
|
addLastAccessedKey(key);
|
|
579
|
-
}
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
580
523
|
removeLastAccessedKey(key);
|
|
581
524
|
}
|
|
582
|
-
|
|
583
525
|
// We are iterating over all subscribers to see if they are interested in the key that has just changed. If the subscriber's key is a collection key then we will
|
|
584
526
|
// notify them if the key that changed is a collection member. Or if it is a regular key notify them when there is an exact match. Depending on whether the subscriber
|
|
585
527
|
// was connected via withOnyx we will call setState() directly on the withOnyx instance. If it is a regular connection we will pass the data to the provided callback.
|
|
586
|
-
const stateMappingKeys =
|
|
528
|
+
const stateMappingKeys = underscore_1.default.keys(callbackToStateMapping);
|
|
587
529
|
for (let i = 0; i < stateMappingKeys.length; i++) {
|
|
588
530
|
const subscriber = callbackToStateMapping[stateMappingKeys[i]];
|
|
589
|
-
if (!subscriber || !isKeyMatch(subscriber.key, key) || (
|
|
531
|
+
if (!subscriber || !isKeyMatch(subscriber.key, key) || (underscore_1.default.isFunction(canUpdateSubscriber) && !canUpdateSubscriber(subscriber))) {
|
|
590
532
|
continue;
|
|
591
533
|
}
|
|
592
|
-
|
|
593
534
|
// Subscriber is a regular call to connect() and provided a callback
|
|
594
|
-
if (
|
|
535
|
+
if (underscore_1.default.isFunction(subscriber.callback)) {
|
|
595
536
|
if (!notifyRegularSubscibers) {
|
|
596
537
|
continue;
|
|
597
538
|
}
|
|
@@ -601,17 +542,14 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
601
542
|
subscriber.callback(cachedCollection);
|
|
602
543
|
continue;
|
|
603
544
|
}
|
|
604
|
-
|
|
605
545
|
subscriber.callback(data, key);
|
|
606
546
|
continue;
|
|
607
547
|
}
|
|
608
|
-
|
|
609
548
|
// Subscriber connected via withOnyx() HOC
|
|
610
549
|
if (subscriber.withOnyxInstance) {
|
|
611
550
|
if (!notifyWithOnyxSubscibers) {
|
|
612
551
|
continue;
|
|
613
552
|
}
|
|
614
|
-
|
|
615
553
|
// Check if we are subscribing to a collection key and overwrite the collection member key value in state
|
|
616
554
|
if (isCollectionKey(subscriber.key)) {
|
|
617
555
|
// If the subscriber has a selector, then the consumer of this data must only be given the data
|
|
@@ -622,11 +560,8 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
622
560
|
const newData = {
|
|
623
561
|
[key]: getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state),
|
|
624
562
|
};
|
|
625
|
-
const prevDataWithNewData = {
|
|
626
|
-
|
|
627
|
-
...newData,
|
|
628
|
-
};
|
|
629
|
-
if (!deepEqual(prevData, prevDataWithNewData)) {
|
|
563
|
+
const prevDataWithNewData = Object.assign(Object.assign({}, prevData), newData);
|
|
564
|
+
if (!(0, fast_equals_1.deepEqual)(prevData, prevDataWithNewData)) {
|
|
630
565
|
PerformanceUtils.logSetStateCall(subscriber, prevData, newData, 'keyChanged', key);
|
|
631
566
|
return {
|
|
632
567
|
[subscriber.statePropertyName]: prevDataWithNewData,
|
|
@@ -636,13 +571,9 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
636
571
|
});
|
|
637
572
|
continue;
|
|
638
573
|
}
|
|
639
|
-
|
|
640
574
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
641
575
|
const collection = prevState[subscriber.statePropertyName] || {};
|
|
642
|
-
const newCollection = {
|
|
643
|
-
...collection,
|
|
644
|
-
[key]: data,
|
|
645
|
-
};
|
|
576
|
+
const newCollection = Object.assign(Object.assign({}, collection), { [key]: data });
|
|
646
577
|
PerformanceUtils.logSetStateCall(subscriber, collection, newCollection, 'keyChanged', key);
|
|
647
578
|
return {
|
|
648
579
|
[subscriber.statePropertyName]: newCollection,
|
|
@@ -650,14 +581,13 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
650
581
|
});
|
|
651
582
|
continue;
|
|
652
583
|
}
|
|
653
|
-
|
|
654
584
|
// If the subscriber has a selector, then the component's state must only be updated with the data
|
|
655
585
|
// returned by the selector and only if the selected data has changed.
|
|
656
586
|
if (subscriber.selector) {
|
|
657
587
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
658
588
|
const previousValue = getSubsetOfData(prevState[subscriber.statePropertyName], subscriber.selector, subscriber.withOnyxInstance.state);
|
|
659
589
|
const newValue = getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state);
|
|
660
|
-
if (!deepEqual(previousValue, newValue)) {
|
|
590
|
+
if (!(0, fast_equals_1.deepEqual)(previousValue, newValue)) {
|
|
661
591
|
return {
|
|
662
592
|
[subscriber.statePropertyName]: newValue,
|
|
663
593
|
};
|
|
@@ -666,19 +596,16 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
666
596
|
});
|
|
667
597
|
continue;
|
|
668
598
|
}
|
|
669
|
-
|
|
670
599
|
// If we did not match on a collection key then we just set the new data to the state property
|
|
671
600
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
672
601
|
const previousData = prevState[subscriber.statePropertyName];
|
|
673
|
-
|
|
674
602
|
// Avoids triggering unnecessary re-renders when feeding empty objects
|
|
675
|
-
if (
|
|
603
|
+
if (utils_1.default.areObjectsEmpty(data, previousData)) {
|
|
676
604
|
return null;
|
|
677
605
|
}
|
|
678
606
|
if (previousData === data) {
|
|
679
607
|
return null;
|
|
680
608
|
}
|
|
681
|
-
|
|
682
609
|
PerformanceUtils.logSetStateCall(subscriber, previousData, data, 'keyChanged', key);
|
|
683
610
|
return {
|
|
684
611
|
[subscriber.statePropertyName]: data,
|
|
@@ -686,11 +613,9 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
686
613
|
});
|
|
687
614
|
continue;
|
|
688
615
|
}
|
|
689
|
-
|
|
690
616
|
console.error('Warning: Found a matching subscriber to a key that changed, but no callback or withOnyxInstance could be found.');
|
|
691
617
|
}
|
|
692
618
|
}
|
|
693
|
-
|
|
694
619
|
/**
|
|
695
620
|
* Sends the data obtained from the keys to the connection. It either:
|
|
696
621
|
* - sets state on the withOnyxInstances
|
|
@@ -712,36 +637,33 @@ function sendDataToConnection(mapping, val, matchedKey, isBatched) {
|
|
|
712
637
|
if (!callbackToStateMapping[mapping.connectionID]) {
|
|
713
638
|
return;
|
|
714
639
|
}
|
|
715
|
-
|
|
716
640
|
if (mapping.withOnyxInstance) {
|
|
717
641
|
let newData = val;
|
|
718
|
-
|
|
719
642
|
// If the mapping has a selector, then the component's state must only be updated with the data
|
|
720
643
|
// returned by the selector.
|
|
721
644
|
if (mapping.selector) {
|
|
722
645
|
if (isCollectionKey(mapping.key)) {
|
|
723
646
|
newData = reduceCollectionWithSelector(val, mapping.selector, mapping.withOnyxInstance.state);
|
|
724
|
-
}
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
725
649
|
newData = getSubsetOfData(val, mapping.selector, mapping.withOnyxInstance.state);
|
|
726
650
|
}
|
|
727
651
|
}
|
|
728
|
-
|
|
729
652
|
PerformanceUtils.logSetStateCall(mapping, null, newData, 'sendDataToConnection');
|
|
730
653
|
if (isBatched) {
|
|
731
654
|
batchUpdates(() => {
|
|
732
655
|
mapping.withOnyxInstance.setWithOnyxState(mapping.statePropertyName, newData);
|
|
733
656
|
});
|
|
734
|
-
}
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
735
659
|
mapping.withOnyxInstance.setWithOnyxState(mapping.statePropertyName, newData);
|
|
736
660
|
}
|
|
737
661
|
return;
|
|
738
662
|
}
|
|
739
|
-
|
|
740
|
-
if (_.isFunction(mapping.callback)) {
|
|
663
|
+
if (underscore_1.default.isFunction(mapping.callback)) {
|
|
741
664
|
mapping.callback(val, matchedKey);
|
|
742
665
|
}
|
|
743
666
|
}
|
|
744
|
-
|
|
745
667
|
/**
|
|
746
668
|
* We check to see if this key is flagged as safe for eviction and add it to the recentlyAccessedKeys list so that when we
|
|
747
669
|
* run out of storage the least recently accessed key can be removed.
|
|
@@ -753,20 +675,16 @@ function addKeyToRecentlyAccessedIfNeeded(mapping) {
|
|
|
753
675
|
if (!isSafeEvictionKey(mapping.key)) {
|
|
754
676
|
return;
|
|
755
677
|
}
|
|
756
|
-
|
|
757
678
|
// Try to free some cache whenever we connect to a safe eviction key
|
|
758
|
-
|
|
759
|
-
|
|
679
|
+
OnyxCache_1.default.removeLeastRecentlyUsedKeys();
|
|
760
680
|
if (mapping.withOnyxInstance && !isCollectionKey(mapping.key)) {
|
|
761
681
|
// All React components subscribing to a key flagged as a safe eviction key must implement the canEvict property.
|
|
762
|
-
if (
|
|
682
|
+
if (underscore_1.default.isUndefined(mapping.canEvict)) {
|
|
763
683
|
throw new Error(`Cannot subscribe to safe eviction key '${mapping.key}' without providing a canEvict value.`);
|
|
764
684
|
}
|
|
765
|
-
|
|
766
685
|
addLastAccessedKey(mapping.key);
|
|
767
686
|
}
|
|
768
687
|
}
|
|
769
|
-
|
|
770
688
|
/**
|
|
771
689
|
* Gets the data for a given an array of matching keys, combines them into an object, and sends the result back to the subscriber.
|
|
772
690
|
*
|
|
@@ -775,21 +693,14 @@ function addKeyToRecentlyAccessedIfNeeded(mapping) {
|
|
|
775
693
|
* @param {Object} mapping
|
|
776
694
|
*/
|
|
777
695
|
function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
|
|
778
|
-
Promise.all(
|
|
779
|
-
.then((values) =>
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
finalObject[matchingKeys[i]] = value;
|
|
785
|
-
return finalObject;
|
|
786
|
-
},
|
|
787
|
-
{},
|
|
788
|
-
),
|
|
789
|
-
)
|
|
696
|
+
Promise.all(underscore_1.default.map(matchingKeys, (key) => get(key)))
|
|
697
|
+
.then((values) => underscore_1.default.reduce(values, (finalObject, value, i) => {
|
|
698
|
+
// eslint-disable-next-line no-param-reassign
|
|
699
|
+
finalObject[matchingKeys[i]] = value;
|
|
700
|
+
return finalObject;
|
|
701
|
+
}, {}))
|
|
790
702
|
.then((val) => sendDataToConnection(mapping, val, undefined, true));
|
|
791
703
|
}
|
|
792
|
-
|
|
793
704
|
/**
|
|
794
705
|
* Subscribes a react component's state directly to a store key
|
|
795
706
|
*
|
|
@@ -822,87 +733,75 @@ function connect(mapping) {
|
|
|
822
733
|
const connectionID = lastConnectionID++;
|
|
823
734
|
callbackToStateMapping[connectionID] = mapping;
|
|
824
735
|
callbackToStateMapping[connectionID].connectionID = connectionID;
|
|
825
|
-
|
|
826
736
|
if (mapping.initWithStoredValues === false) {
|
|
827
737
|
return connectionID;
|
|
828
738
|
}
|
|
829
|
-
|
|
830
739
|
// Commit connection only after init passes
|
|
831
740
|
deferredInitTask.promise
|
|
832
741
|
.then(() => addKeyToRecentlyAccessedIfNeeded(mapping))
|
|
833
742
|
.then(() => {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
743
|
+
// Performance improvement
|
|
744
|
+
// If the mapping is connected to an onyx key that is not a collection
|
|
745
|
+
// we can skip the call to getAllKeys() and return an array with a single item
|
|
746
|
+
if (Boolean(mapping.key) && typeof mapping.key === 'string' && !mapping.key.endsWith('_') && OnyxCache_1.default.storageKeys.has(mapping.key)) {
|
|
747
|
+
return [mapping.key];
|
|
748
|
+
}
|
|
749
|
+
return getAllKeys();
|
|
750
|
+
})
|
|
842
751
|
.then((keys) => {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
if (
|
|
853
|
-
|
|
854
|
-
cache.set(mapping.key, null);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// Here we cannot use batching because the null value is expected to be set immediately for default props
|
|
858
|
-
// or they will be undefined.
|
|
859
|
-
sendDataToConnection(mapping, null, undefined, false);
|
|
860
|
-
return;
|
|
752
|
+
// We search all the keys in storage to see if any are a "match" for the subscriber we are connecting so that we
|
|
753
|
+
// can send data back to the subscriber. Note that multiple keys can match as a subscriber could either be
|
|
754
|
+
// subscribed to a "collection key" or a single key.
|
|
755
|
+
const matchingKeys = underscore_1.default.filter(keys, (key) => isKeyMatch(mapping.key, key));
|
|
756
|
+
// If the key being connected to does not exist we initialize the value with null. For subscribers that connected
|
|
757
|
+
// directly via connect() they will simply get a null value sent to them without any information about which key matched
|
|
758
|
+
// since there are none matched. In withOnyx() we wait for all connected keys to return a value before rendering the child
|
|
759
|
+
// component. This null value will be filtered out so that the connected component can utilize defaultProps.
|
|
760
|
+
if (matchingKeys.length === 0) {
|
|
761
|
+
if (mapping.key && !isCollectionKey(mapping.key)) {
|
|
762
|
+
OnyxCache_1.default.set(mapping.key, null);
|
|
861
763
|
}
|
|
862
|
-
|
|
863
|
-
//
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
for (let i = 0; i < matchingKeys.length; i++) {
|
|
875
|
-
get(matchingKeys[i]).then((val) => sendDataToConnection(mapping, val, matchingKeys[i], true));
|
|
876
|
-
}
|
|
764
|
+
// Here we cannot use batching because the null value is expected to be set immediately for default props
|
|
765
|
+
// or they will be undefined.
|
|
766
|
+
sendDataToConnection(mapping, null, undefined, false);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
// When using a callback subscriber we will either trigger the provided callback for each key we find or combine all values
|
|
770
|
+
// into an object and just make a single call. The latter behavior is enabled by providing a waitForCollectionCallback key
|
|
771
|
+
// combined with a subscription to a collection key.
|
|
772
|
+
if (underscore_1.default.isFunction(mapping.callback)) {
|
|
773
|
+
if (isCollectionKey(mapping.key)) {
|
|
774
|
+
if (mapping.waitForCollectionCallback) {
|
|
775
|
+
getCollectionDataAndSendAsObject(matchingKeys, mapping);
|
|
877
776
|
return;
|
|
878
777
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
778
|
+
// We did not opt into using waitForCollectionCallback mode so the callback is called for every matching key.
|
|
779
|
+
for (let i = 0; i < matchingKeys.length; i++) {
|
|
780
|
+
get(matchingKeys[i]).then((val) => sendDataToConnection(mapping, val, matchingKeys[i], true));
|
|
781
|
+
}
|
|
882
782
|
return;
|
|
883
783
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
// If the subscriber is not using a collection key then we just send a single value back to the subscriber
|
|
894
|
-
get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key, true));
|
|
784
|
+
// If we are not subscribed to a collection key then there's only a single key to send an update for.
|
|
785
|
+
get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key, true));
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
// If we have a withOnyxInstance that means a React component has subscribed via the withOnyx() HOC and we need to
|
|
789
|
+
// group collection key member data into an object.
|
|
790
|
+
if (mapping.withOnyxInstance) {
|
|
791
|
+
if (isCollectionKey(mapping.key)) {
|
|
792
|
+
getCollectionDataAndSendAsObject(matchingKeys, mapping);
|
|
895
793
|
return;
|
|
896
794
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
795
|
+
// If the subscriber is not using a collection key then we just send a single value back to the subscriber
|
|
796
|
+
get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key, true));
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
console.error('Warning: Onyx.connect() was found without a callback or withOnyxInstance');
|
|
800
|
+
});
|
|
901
801
|
// The connectionID is returned back to the caller so that it can be used to clean up the connection when it's no longer needed
|
|
902
802
|
// by calling Onyx.disconnect(connectionID).
|
|
903
803
|
return connectionID;
|
|
904
804
|
}
|
|
905
|
-
|
|
906
805
|
/**
|
|
907
806
|
* Remove the listener for a react component
|
|
908
807
|
* @example
|
|
@@ -915,16 +814,13 @@ function disconnect(connectionID, keyToRemoveFromEvictionBlocklist) {
|
|
|
915
814
|
if (!callbackToStateMapping[connectionID]) {
|
|
916
815
|
return;
|
|
917
816
|
}
|
|
918
|
-
|
|
919
817
|
// Remove this key from the eviction block list as we are no longer
|
|
920
818
|
// subscribing to it and it should be safe to delete again
|
|
921
819
|
if (keyToRemoveFromEvictionBlocklist) {
|
|
922
820
|
removeFromEvictionBlockList(keyToRemoveFromEvictionBlocklist, connectionID);
|
|
923
821
|
}
|
|
924
|
-
|
|
925
822
|
delete callbackToStateMapping[connectionID];
|
|
926
823
|
}
|
|
927
|
-
|
|
928
824
|
/**
|
|
929
825
|
* Schedules an update that will be appended to the macro task queue (so it doesn't update the subscribers immediately).
|
|
930
826
|
*
|
|
@@ -941,7 +837,6 @@ function scheduleSubscriberUpdate(key, value, canUpdateSubscriber) {
|
|
|
941
837
|
batchUpdates(() => keyChanged(key, value, canUpdateSubscriber, false, true));
|
|
942
838
|
return Promise.all([maybeFlushBatchUpdates(), promise]);
|
|
943
839
|
}
|
|
944
|
-
|
|
945
840
|
/**
|
|
946
841
|
* This method is similar to notifySubscribersOnNextTick but it is built for working specifically with collections
|
|
947
842
|
* so that keysChanged() is triggered for the collection and not keyChanged(). If this was not done, then the
|
|
@@ -956,7 +851,6 @@ function scheduleNotifyCollectionSubscribers(key, value) {
|
|
|
956
851
|
batchUpdates(() => keysChanged(key, value, false, true));
|
|
957
852
|
return Promise.all([maybeFlushBatchUpdates(), promise]);
|
|
958
853
|
}
|
|
959
|
-
|
|
960
854
|
/**
|
|
961
855
|
* Remove a key from Onyx and update the subscribers
|
|
962
856
|
*
|
|
@@ -965,25 +859,23 @@ function scheduleNotifyCollectionSubscribers(key, value) {
|
|
|
965
859
|
* @return {Promise}
|
|
966
860
|
*/
|
|
967
861
|
function remove(key) {
|
|
968
|
-
|
|
862
|
+
OnyxCache_1.default.drop(key);
|
|
969
863
|
scheduleSubscriberUpdate(key, null);
|
|
970
|
-
return
|
|
864
|
+
return storage_1.default.removeItem(key);
|
|
971
865
|
}
|
|
972
|
-
|
|
973
866
|
/**
|
|
974
867
|
* @private
|
|
975
868
|
* @returns {Promise<void>}
|
|
976
869
|
*/
|
|
977
870
|
function reportStorageQuota() {
|
|
978
|
-
return
|
|
979
|
-
.then(({bytesUsed, bytesRemaining}) => {
|
|
980
|
-
|
|
981
|
-
|
|
871
|
+
return storage_1.default.getDatabaseSize()
|
|
872
|
+
.then(({ bytesUsed, bytesRemaining }) => {
|
|
873
|
+
Logger.logInfo(`Storage Quota Check -- bytesUsed: ${bytesUsed} bytesRemaining: ${bytesRemaining}`);
|
|
874
|
+
})
|
|
982
875
|
.catch((dbSizeError) => {
|
|
983
|
-
|
|
984
|
-
|
|
876
|
+
Logger.logAlert(`Unable to get database size. Error: ${dbSizeError}`);
|
|
877
|
+
});
|
|
985
878
|
}
|
|
986
|
-
|
|
987
879
|
/**
|
|
988
880
|
* If we fail to set or merge we must handle this by
|
|
989
881
|
* evicting some data from Onyx and then retrying to do
|
|
@@ -997,14 +889,12 @@ function reportStorageQuota() {
|
|
|
997
889
|
*/
|
|
998
890
|
function evictStorageAndRetry(error, onyxMethod, ...args) {
|
|
999
891
|
Logger.logInfo(`Failed to save to storage. Error: ${error}. onyxMethod: ${onyxMethod.name}`);
|
|
1000
|
-
|
|
1001
892
|
if (error && Str.startsWith(error.message, "Failed to execute 'put' on 'IDBObjectStore'")) {
|
|
1002
893
|
Logger.logAlert('Attempted to set invalid data set in Onyx. Please ensure all data is serializable.');
|
|
1003
894
|
throw error;
|
|
1004
895
|
}
|
|
1005
|
-
|
|
1006
896
|
// Find the first key that we can remove that has no subscribers in our blocklist
|
|
1007
|
-
const keyForRemoval =
|
|
897
|
+
const keyForRemoval = underscore_1.default.find(recentlyAccessedKeys, (key) => !evictionBlocklist[key]);
|
|
1008
898
|
if (!keyForRemoval) {
|
|
1009
899
|
// If we have no acceptable keys to remove then we are possibly trying to save mission critical data. If this is the case,
|
|
1010
900
|
// then we should stop retrying as there is not much the user can do to fix this. Instead of getting them stuck in an infinite loop we
|
|
@@ -1012,13 +902,11 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
|
|
|
1012
902
|
Logger.logAlert('Out of storage. But found no acceptable keys to remove.');
|
|
1013
903
|
return reportStorageQuota();
|
|
1014
904
|
}
|
|
1015
|
-
|
|
1016
905
|
// Remove the least recently viewed key that is not currently being accessed and retry.
|
|
1017
906
|
Logger.logInfo(`Out of storage. Evicting least recently accessed key (${keyForRemoval}) and retrying.`);
|
|
1018
907
|
reportStorageQuota();
|
|
1019
908
|
return remove(keyForRemoval).then(() => onyxMethod(...args));
|
|
1020
909
|
}
|
|
1021
|
-
|
|
1022
910
|
/**
|
|
1023
911
|
* Notifys subscribers and writes current value to cache
|
|
1024
912
|
*
|
|
@@ -1031,19 +919,17 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
|
|
|
1031
919
|
*/
|
|
1032
920
|
function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) {
|
|
1033
921
|
// Logging properties only since values could be sensitive things we don't want to log
|
|
1034
|
-
Logger.logInfo(`${method}() called for key: ${key}${
|
|
1035
|
-
|
|
922
|
+
Logger.logInfo(`${method}() called for key: ${key}${underscore_1.default.isObject(value) ? ` properties: ${underscore_1.default.keys(value).join(',')}` : ''}`);
|
|
1036
923
|
// Update subscribers if the cached value has changed, or when the subscriber specifically requires
|
|
1037
924
|
// all updates regardless of value changes (indicated by initWithStoredValues set to false).
|
|
1038
925
|
if (hasChanged && !wasRemoved) {
|
|
1039
|
-
|
|
1040
|
-
}
|
|
1041
|
-
|
|
926
|
+
OnyxCache_1.default.set(key, value);
|
|
927
|
+
}
|
|
928
|
+
else {
|
|
929
|
+
OnyxCache_1.default.addToAccessedKeys(key);
|
|
1042
930
|
}
|
|
1043
|
-
|
|
1044
931
|
return scheduleSubscriberUpdate(key, value, (subscriber) => hasChanged || subscriber.initWithStoredValues === false);
|
|
1045
932
|
}
|
|
1046
|
-
|
|
1047
933
|
/**
|
|
1048
934
|
* @param {String} key
|
|
1049
935
|
* @returns {Boolean}
|
|
@@ -1051,7 +937,6 @@ function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) {
|
|
|
1051
937
|
function hasPendingMergeForKey(key) {
|
|
1052
938
|
return Boolean(mergeQueue[key]);
|
|
1053
939
|
}
|
|
1054
|
-
|
|
1055
940
|
/**
|
|
1056
941
|
* Removes a key from storage if the value is null.
|
|
1057
942
|
* Otherwise removes all nested null values in objects and returns the object
|
|
@@ -1060,17 +945,15 @@ function hasPendingMergeForKey(key) {
|
|
|
1060
945
|
* @returns {Mixed} The value without null values and a boolean "wasRemoved", which indicates if the key got removed completely
|
|
1061
946
|
*/
|
|
1062
947
|
function removeNullValues(key, value) {
|
|
1063
|
-
if (
|
|
948
|
+
if (underscore_1.default.isNull(value)) {
|
|
1064
949
|
remove(key);
|
|
1065
|
-
return {value, wasRemoved: true};
|
|
950
|
+
return { value, wasRemoved: true };
|
|
1066
951
|
}
|
|
1067
|
-
|
|
1068
952
|
// We can remove all null values in an object by merging it with itself
|
|
1069
953
|
// utils.fastMerge recursively goes through the object and removes all null values
|
|
1070
954
|
// Passing two identical objects as source and target to fastMerge will not change it, but only remove the null values
|
|
1071
|
-
return {value:
|
|
955
|
+
return { value: utils_1.default.removeNestedNullValues(value), wasRemoved: false };
|
|
1072
956
|
}
|
|
1073
|
-
|
|
1074
957
|
/**
|
|
1075
958
|
* Write a value to our store with the given key
|
|
1076
959
|
*
|
|
@@ -1081,30 +964,24 @@ function removeNullValues(key, value) {
|
|
|
1081
964
|
*/
|
|
1082
965
|
function set(key, value) {
|
|
1083
966
|
// If the value is null, we remove the key from storage
|
|
1084
|
-
const {value: valueAfterRemoving, wasRemoved} = removeNullValues(key, value);
|
|
1085
|
-
|
|
967
|
+
const { value: valueAfterRemoving, wasRemoved } = removeNullValues(key, value);
|
|
1086
968
|
if (hasPendingMergeForKey(key)) {
|
|
1087
969
|
delete mergeQueue[key];
|
|
1088
970
|
}
|
|
1089
|
-
|
|
1090
|
-
const hasChanged = cache.hasValueChanged(key, valueAfterRemoving);
|
|
1091
|
-
|
|
971
|
+
const hasChanged = OnyxCache_1.default.hasValueChanged(key, valueAfterRemoving);
|
|
1092
972
|
// This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
|
|
1093
973
|
const updatePromise = broadcastUpdate(key, valueAfterRemoving, 'set', hasChanged, wasRemoved);
|
|
1094
|
-
|
|
1095
974
|
// If the value has not changed or the key got removed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
|
|
1096
975
|
if (!hasChanged || wasRemoved) {
|
|
1097
976
|
return updatePromise;
|
|
1098
977
|
}
|
|
1099
|
-
|
|
1100
|
-
return Storage.setItem(key, valueAfterRemoving)
|
|
978
|
+
return storage_1.default.setItem(key, valueAfterRemoving)
|
|
1101
979
|
.catch((error) => evictStorageAndRetry(error, set, key, valueAfterRemoving))
|
|
1102
980
|
.then(() => {
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
981
|
+
sendActionToDevTools(METHOD.SET, key, valueAfterRemoving);
|
|
982
|
+
return updatePromise;
|
|
983
|
+
});
|
|
1106
984
|
}
|
|
1107
|
-
|
|
1108
985
|
/**
|
|
1109
986
|
* Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
|
|
1110
987
|
* This method transforms an object like {'@MyApp_user': myUserValue, '@MyApp_key': myKeyValue}
|
|
@@ -1115,18 +992,14 @@ function set(key, value) {
|
|
|
1115
992
|
*/
|
|
1116
993
|
function prepareKeyValuePairsForStorage(data) {
|
|
1117
994
|
const keyValuePairs = [];
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
if (wasRemoved) return;
|
|
1123
|
-
|
|
995
|
+
underscore_1.default.forEach(data, (value, key) => {
|
|
996
|
+
const { value: valueAfterRemoving, wasRemoved } = removeNullValues(key, value);
|
|
997
|
+
if (wasRemoved)
|
|
998
|
+
return;
|
|
1124
999
|
keyValuePairs.push([key, valueAfterRemoving]);
|
|
1125
1000
|
});
|
|
1126
|
-
|
|
1127
1001
|
return keyValuePairs;
|
|
1128
1002
|
}
|
|
1129
|
-
|
|
1130
1003
|
/**
|
|
1131
1004
|
* Sets multiple keys and values
|
|
1132
1005
|
*
|
|
@@ -1137,21 +1010,18 @@ function prepareKeyValuePairsForStorage(data) {
|
|
|
1137
1010
|
*/
|
|
1138
1011
|
function multiSet(data) {
|
|
1139
1012
|
const keyValuePairs = prepareKeyValuePairsForStorage(data);
|
|
1140
|
-
|
|
1141
|
-
const updatePromises = _.map(keyValuePairs, ([key, value]) => {
|
|
1013
|
+
const updatePromises = underscore_1.default.map(keyValuePairs, ([key, value]) => {
|
|
1142
1014
|
// Update cache and optimistically inform subscribers on the next tick
|
|
1143
|
-
|
|
1015
|
+
OnyxCache_1.default.set(key, value);
|
|
1144
1016
|
return scheduleSubscriberUpdate(key, value);
|
|
1145
1017
|
});
|
|
1146
|
-
|
|
1147
|
-
return Storage.multiSet(keyValuePairs)
|
|
1018
|
+
return storage_1.default.multiSet(keyValuePairs)
|
|
1148
1019
|
.catch((error) => evictStorageAndRetry(error, multiSet, data))
|
|
1149
1020
|
.then(() => {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1021
|
+
sendActionToDevTools(METHOD.MULTI_SET, undefined, data);
|
|
1022
|
+
return Promise.all(updatePromises);
|
|
1023
|
+
});
|
|
1153
1024
|
}
|
|
1154
|
-
|
|
1155
1025
|
/**
|
|
1156
1026
|
* Merges an array of changes with an existing value
|
|
1157
1027
|
*
|
|
@@ -1162,22 +1032,18 @@ function multiSet(data) {
|
|
|
1162
1032
|
* @returns {*}
|
|
1163
1033
|
*/
|
|
1164
1034
|
function applyMerge(existingValue, changes, shouldRemoveNullObjectValues) {
|
|
1165
|
-
const lastChange =
|
|
1166
|
-
|
|
1167
|
-
if (_.isArray(lastChange)) {
|
|
1035
|
+
const lastChange = underscore_1.default.last(changes);
|
|
1036
|
+
if (underscore_1.default.isArray(lastChange)) {
|
|
1168
1037
|
return lastChange;
|
|
1169
1038
|
}
|
|
1170
|
-
|
|
1171
|
-
if (_.some(changes, _.isObject)) {
|
|
1039
|
+
if (underscore_1.default.some(changes, underscore_1.default.isObject)) {
|
|
1172
1040
|
// Object values are then merged one after the other
|
|
1173
|
-
return
|
|
1041
|
+
return underscore_1.default.reduce(changes, (modifiedData, change) => utils_1.default.fastMerge(modifiedData, change, shouldRemoveNullObjectValues), existingValue || {});
|
|
1174
1042
|
}
|
|
1175
|
-
|
|
1176
1043
|
// If we have anything else we can't merge it so we'll
|
|
1177
1044
|
// simply return the last value that was queued
|
|
1178
1045
|
return lastChange;
|
|
1179
1046
|
}
|
|
1180
|
-
|
|
1181
1047
|
/**
|
|
1182
1048
|
* Merge a new value into an existing value at a key.
|
|
1183
1049
|
*
|
|
@@ -1201,10 +1067,9 @@ function applyMerge(existingValue, changes, shouldRemoveNullObjectValues) {
|
|
|
1201
1067
|
function merge(key, changes) {
|
|
1202
1068
|
// Top-level undefined values are ignored
|
|
1203
1069
|
// Therefore we need to prevent adding them to the merge queue
|
|
1204
|
-
if (
|
|
1070
|
+
if (underscore_1.default.isUndefined(changes)) {
|
|
1205
1071
|
return mergeQueue[key] ? mergeQueuePromise[key] : Promise.resolve();
|
|
1206
1072
|
}
|
|
1207
|
-
|
|
1208
1073
|
// Merge attempts are batched together. The delta should be applied after a single call to get() to prevent a race condition.
|
|
1209
1074
|
// Using the initial value from storage in subsequent merge attempts will lead to an incorrect final merged value.
|
|
1210
1075
|
if (mergeQueue[key]) {
|
|
@@ -1212,32 +1077,26 @@ function merge(key, changes) {
|
|
|
1212
1077
|
return mergeQueuePromise[key];
|
|
1213
1078
|
}
|
|
1214
1079
|
mergeQueue[key] = [changes];
|
|
1215
|
-
|
|
1216
1080
|
mergeQueuePromise[key] = get(key).then((existingValue) => {
|
|
1217
1081
|
// Calls to Onyx.set after a merge will terminate the current merge process and clear the merge queue
|
|
1218
|
-
if (mergeQueue[key] == null)
|
|
1219
|
-
|
|
1082
|
+
if (mergeQueue[key] == null)
|
|
1083
|
+
return;
|
|
1220
1084
|
try {
|
|
1221
1085
|
// 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)
|
|
1222
1086
|
// We don't want to remove null values from the "batchedChanges", because SQLite uses them to remove keys from storage natively.
|
|
1223
1087
|
let batchedChanges = applyMerge(undefined, mergeQueue[key], false);
|
|
1224
|
-
|
|
1225
1088
|
// The presence of a `null` in the merge queue instructs us to drop the existing value.
|
|
1226
1089
|
// In this case, we can't simply merge the batched changes with the existing value, because then the null in the merge queue would have no effect
|
|
1227
|
-
const shouldOverwriteExistingValue =
|
|
1228
|
-
|
|
1090
|
+
const shouldOverwriteExistingValue = underscore_1.default.includes(mergeQueue[key], null);
|
|
1229
1091
|
// Clean up the write queue, so we don't apply these changes again
|
|
1230
1092
|
delete mergeQueue[key];
|
|
1231
1093
|
delete mergeQueuePromise[key];
|
|
1232
|
-
|
|
1233
1094
|
// If the batched changes equal null, we want to remove the key from storage, to reduce storage size
|
|
1234
|
-
const {wasRemoved} = removeNullValues(key, batchedChanges);
|
|
1235
|
-
|
|
1095
|
+
const { wasRemoved } = removeNullValues(key, batchedChanges);
|
|
1236
1096
|
// After that we merge the batched changes with the existing value
|
|
1237
1097
|
// We can remove null values from the "modifiedData", because "null" implicates that the user wants to remove a value from storage.
|
|
1238
1098
|
// The "modifiedData" will be directly "set" in storage instead of being merged
|
|
1239
1099
|
const modifiedData = shouldOverwriteExistingValue ? batchedChanges : applyMerge(existingValue, [batchedChanges], true);
|
|
1240
|
-
|
|
1241
1100
|
// On native platforms we use SQLite which utilises JSON_PATCH to merge changes.
|
|
1242
1101
|
// JSON_PATCH generally removes null values from the stored object.
|
|
1243
1102
|
// When there is no existing value though, SQLite will just insert the changes as a new value and thus the null values won't be removed.
|
|
@@ -1245,45 +1104,38 @@ function merge(key, changes) {
|
|
|
1245
1104
|
if (!existingValue) {
|
|
1246
1105
|
batchedChanges = applyMerge(undefined, [batchedChanges], true);
|
|
1247
1106
|
}
|
|
1248
|
-
|
|
1249
|
-
const hasChanged = cache.hasValueChanged(key, modifiedData);
|
|
1250
|
-
|
|
1107
|
+
const hasChanged = OnyxCache_1.default.hasValueChanged(key, modifiedData);
|
|
1251
1108
|
// This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
|
|
1252
1109
|
const updatePromise = broadcastUpdate(key, modifiedData, 'merge', hasChanged, wasRemoved);
|
|
1253
|
-
|
|
1254
1110
|
// If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
|
|
1255
1111
|
if (!hasChanged || wasRemoved) {
|
|
1256
1112
|
return updatePromise;
|
|
1257
1113
|
}
|
|
1258
|
-
|
|
1259
|
-
return Storage.mergeItem(key, batchedChanges, modifiedData).then(() => {
|
|
1114
|
+
return storage_1.default.mergeItem(key, batchedChanges, modifiedData).then(() => {
|
|
1260
1115
|
sendActionToDevTools(METHOD.MERGE, key, changes, modifiedData);
|
|
1261
1116
|
return updatePromise;
|
|
1262
1117
|
});
|
|
1263
|
-
}
|
|
1118
|
+
}
|
|
1119
|
+
catch (error) {
|
|
1264
1120
|
Logger.logAlert(`An error occurred while applying merge for key: ${key}, Error: ${error}`);
|
|
1265
1121
|
return Promise.resolve();
|
|
1266
1122
|
}
|
|
1267
1123
|
});
|
|
1268
|
-
|
|
1269
1124
|
return mergeQueuePromise[key];
|
|
1270
1125
|
}
|
|
1271
|
-
|
|
1272
1126
|
/**
|
|
1273
1127
|
* Merge user provided default key value pairs.
|
|
1274
1128
|
* @private
|
|
1275
1129
|
* @returns {Promise}
|
|
1276
1130
|
*/
|
|
1277
1131
|
function initializeWithDefaultKeyStates() {
|
|
1278
|
-
return
|
|
1279
|
-
const asObject =
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
_.each(merged, (val, key) => keyChanged(key, val));
|
|
1132
|
+
return storage_1.default.multiGet(underscore_1.default.keys(defaultKeyStates)).then((pairs) => {
|
|
1133
|
+
const asObject = underscore_1.default.object(pairs);
|
|
1134
|
+
const merged = utils_1.default.fastMerge(asObject, defaultKeyStates);
|
|
1135
|
+
OnyxCache_1.default.merge(merged);
|
|
1136
|
+
underscore_1.default.each(merged, (val, key) => keyChanged(key, val));
|
|
1284
1137
|
});
|
|
1285
1138
|
}
|
|
1286
|
-
|
|
1287
1139
|
/**
|
|
1288
1140
|
* Clear out all the data in the store
|
|
1289
1141
|
*
|
|
@@ -1311,68 +1163,60 @@ function clear(keysToPreserve = []) {
|
|
|
1311
1163
|
const keysToBeClearedFromStorage = [];
|
|
1312
1164
|
const keyValuesToResetAsCollection = {};
|
|
1313
1165
|
const keyValuesToResetIndividually = {};
|
|
1314
|
-
|
|
1315
1166
|
// The only keys that should not be cleared are:
|
|
1316
1167
|
// 1. Anything specifically passed in keysToPreserve (because some keys like language preferences, offline
|
|
1317
1168
|
// status, or activeClients need to remain in Onyx even when signed out)
|
|
1318
1169
|
// 2. Any keys with a default state (because they need to remain in Onyx as their default, and setting them
|
|
1319
1170
|
// to null would cause unknown behavior)
|
|
1320
|
-
|
|
1321
|
-
const isKeyToPreserve =
|
|
1322
|
-
const isDefaultKey =
|
|
1323
|
-
|
|
1171
|
+
underscore_1.default.each(keys, (key) => {
|
|
1172
|
+
const isKeyToPreserve = underscore_1.default.contains(keysToPreserve, key);
|
|
1173
|
+
const isDefaultKey = underscore_1.default.has(defaultKeyStates, key);
|
|
1324
1174
|
// If the key is being removed or reset to default:
|
|
1325
1175
|
// 1. Update it in the cache
|
|
1326
1176
|
// 2. Figure out whether it is a collection key or not,
|
|
1327
1177
|
// since collection key subscribers need to be updated differently
|
|
1328
1178
|
if (!isKeyToPreserve) {
|
|
1329
|
-
const oldValue =
|
|
1330
|
-
const newValue =
|
|
1179
|
+
const oldValue = OnyxCache_1.default.getValue(key);
|
|
1180
|
+
const newValue = underscore_1.default.get(defaultKeyStates, key, null);
|
|
1331
1181
|
if (newValue !== oldValue) {
|
|
1332
|
-
|
|
1182
|
+
OnyxCache_1.default.set(key, newValue);
|
|
1333
1183
|
const collectionKey = key.substring(0, key.indexOf('_') + 1);
|
|
1334
1184
|
if (collectionKey) {
|
|
1335
1185
|
if (!keyValuesToResetAsCollection[collectionKey]) {
|
|
1336
1186
|
keyValuesToResetAsCollection[collectionKey] = {};
|
|
1337
1187
|
}
|
|
1338
1188
|
keyValuesToResetAsCollection[collectionKey][key] = newValue;
|
|
1339
|
-
}
|
|
1189
|
+
}
|
|
1190
|
+
else {
|
|
1340
1191
|
keyValuesToResetIndividually[key] = newValue;
|
|
1341
1192
|
}
|
|
1342
1193
|
}
|
|
1343
1194
|
}
|
|
1344
|
-
|
|
1345
1195
|
if (isKeyToPreserve || isDefaultKey) {
|
|
1346
1196
|
return;
|
|
1347
1197
|
}
|
|
1348
|
-
|
|
1349
1198
|
// If it isn't preserved and doesn't have a default, we'll remove it
|
|
1350
1199
|
keysToBeClearedFromStorage.push(key);
|
|
1351
1200
|
});
|
|
1352
|
-
|
|
1353
1201
|
const updatePromises = [];
|
|
1354
|
-
|
|
1355
1202
|
// Notify the subscribers for each key/value group so they can receive the new values
|
|
1356
|
-
|
|
1203
|
+
underscore_1.default.each(keyValuesToResetIndividually, (value, key) => {
|
|
1357
1204
|
updatePromises.push(scheduleSubscriberUpdate(key, value));
|
|
1358
1205
|
});
|
|
1359
|
-
|
|
1206
|
+
underscore_1.default.each(keyValuesToResetAsCollection, (value, key) => {
|
|
1360
1207
|
updatePromises.push(scheduleNotifyCollectionSubscribers(key, value));
|
|
1361
1208
|
});
|
|
1362
|
-
|
|
1363
|
-
const defaultKeyValuePairs = _.pairs(_.omit(defaultKeyStates, keysToPreserve));
|
|
1364
|
-
|
|
1209
|
+
const defaultKeyValuePairs = underscore_1.default.pairs(underscore_1.default.omit(defaultKeyStates, keysToPreserve));
|
|
1365
1210
|
// Remove only the items that we want cleared from storage, and reset others to default
|
|
1366
|
-
|
|
1367
|
-
return
|
|
1368
|
-
.then(() =>
|
|
1211
|
+
underscore_1.default.each(keysToBeClearedFromStorage, (key) => OnyxCache_1.default.drop(key));
|
|
1212
|
+
return storage_1.default.removeItems(keysToBeClearedFromStorage)
|
|
1213
|
+
.then(() => storage_1.default.multiSet(defaultKeyValuePairs))
|
|
1369
1214
|
.then(() => {
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1215
|
+
DevTools_1.default.clearState(keysToPreserve);
|
|
1216
|
+
return Promise.all(updatePromises);
|
|
1217
|
+
});
|
|
1373
1218
|
});
|
|
1374
1219
|
}
|
|
1375
|
-
|
|
1376
1220
|
/**
|
|
1377
1221
|
* Merges a collection based on their keys
|
|
1378
1222
|
*
|
|
@@ -1388,78 +1232,66 @@ function clear(keysToPreserve = []) {
|
|
|
1388
1232
|
* @returns {Promise}
|
|
1389
1233
|
*/
|
|
1390
1234
|
function mergeCollection(collectionKey, collection) {
|
|
1391
|
-
if (!
|
|
1235
|
+
if (!underscore_1.default.isObject(collection) || underscore_1.default.isArray(collection) || underscore_1.default.isEmpty(collection)) {
|
|
1392
1236
|
Logger.logInfo('mergeCollection() called with invalid or empty value. Skipping this update.');
|
|
1393
1237
|
return Promise.resolve();
|
|
1394
1238
|
}
|
|
1395
|
-
|
|
1396
1239
|
// Confirm all the collection keys belong to the same parent
|
|
1397
1240
|
let hasCollectionKeyCheckFailed = false;
|
|
1398
|
-
|
|
1241
|
+
underscore_1.default.each(collection, (_data, dataKey) => {
|
|
1399
1242
|
if (isKeyMatch(collectionKey, dataKey)) {
|
|
1400
1243
|
return;
|
|
1401
1244
|
}
|
|
1402
|
-
|
|
1403
1245
|
if (process.env.NODE_ENV === 'development') {
|
|
1404
1246
|
throw new Error(`Provided collection doesn't have all its data belonging to the same parent. CollectionKey: ${collectionKey}, DataKey: ${dataKey}`);
|
|
1405
1247
|
}
|
|
1406
|
-
|
|
1407
1248
|
hasCollectionKeyCheckFailed = true;
|
|
1408
1249
|
Logger.logAlert(`Provided collection doesn't have all its data belonging to the same parent. CollectionKey: ${collectionKey}, DataKey: ${dataKey}`);
|
|
1409
1250
|
});
|
|
1410
|
-
|
|
1411
1251
|
// Gracefully handle bad mergeCollection updates so it doesn't block the merge queue
|
|
1412
1252
|
if (hasCollectionKeyCheckFailed) {
|
|
1413
1253
|
return Promise.resolve();
|
|
1414
1254
|
}
|
|
1415
|
-
|
|
1416
1255
|
return getAllKeys().then((persistedKeys) => {
|
|
1417
1256
|
// Split to keys that exist in storage and keys that don't
|
|
1418
|
-
const [existingKeys, newKeys] =
|
|
1257
|
+
const [existingKeys, newKeys] = underscore_1.default.chain(collection)
|
|
1419
1258
|
.pick((value, key) => {
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1259
|
+
if (underscore_1.default.isNull(value)) {
|
|
1260
|
+
remove(key);
|
|
1261
|
+
return false;
|
|
1262
|
+
}
|
|
1263
|
+
return true;
|
|
1264
|
+
})
|
|
1426
1265
|
.keys()
|
|
1427
1266
|
.partition((key) => persistedKeys.includes(key))
|
|
1428
1267
|
.value();
|
|
1429
|
-
|
|
1430
|
-
const
|
|
1431
|
-
const newCollection = _.pick(collection, newKeys);
|
|
1268
|
+
const existingKeyCollection = underscore_1.default.pick(collection, existingKeys);
|
|
1269
|
+
const newCollection = underscore_1.default.pick(collection, newKeys);
|
|
1432
1270
|
const keyValuePairsForExistingCollection = prepareKeyValuePairsForStorage(existingKeyCollection);
|
|
1433
1271
|
const keyValuePairsForNewCollection = prepareKeyValuePairsForStorage(newCollection);
|
|
1434
|
-
|
|
1435
1272
|
const promises = [];
|
|
1436
|
-
|
|
1437
1273
|
// New keys will be added via multiSet while existing keys will be updated using multiMerge
|
|
1438
1274
|
// This is because setting a key that doesn't exist yet with multiMerge will throw errors
|
|
1439
1275
|
if (keyValuePairsForExistingCollection.length > 0) {
|
|
1440
|
-
promises.push(
|
|
1276
|
+
promises.push(storage_1.default.multiMerge(keyValuePairsForExistingCollection));
|
|
1441
1277
|
}
|
|
1442
|
-
|
|
1443
1278
|
if (keyValuePairsForNewCollection.length > 0) {
|
|
1444
|
-
promises.push(
|
|
1279
|
+
promises.push(storage_1.default.multiSet(keyValuePairsForNewCollection));
|
|
1445
1280
|
}
|
|
1446
|
-
|
|
1447
1281
|
// Prefill cache if necessary by calling get() on any existing keys and then merge original data to cache
|
|
1448
1282
|
// and update all subscribers
|
|
1449
|
-
const promiseUpdate = Promise.all(
|
|
1450
|
-
|
|
1283
|
+
const promiseUpdate = Promise.all(underscore_1.default.map(existingKeys, get)).then(() => {
|
|
1284
|
+
OnyxCache_1.default.merge(collection);
|
|
1451
1285
|
return scheduleNotifyCollectionSubscribers(collectionKey, collection);
|
|
1452
1286
|
});
|
|
1453
|
-
|
|
1454
1287
|
return Promise.all(promises)
|
|
1455
1288
|
.catch((error) => evictStorageAndRetry(error, mergeCollection, collection))
|
|
1456
1289
|
.then(() => {
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1290
|
+
sendActionToDevTools(METHOD.MERGE_COLLECTION, undefined, collection);
|
|
1291
|
+
return promiseUpdate;
|
|
1292
|
+
});
|
|
1460
1293
|
});
|
|
1461
1294
|
}
|
|
1462
|
-
|
|
1463
1295
|
/**
|
|
1464
1296
|
* Insert API responses and lifecycle data into Onyx
|
|
1465
1297
|
*
|
|
@@ -1468,24 +1300,23 @@ function mergeCollection(collectionKey, collection) {
|
|
|
1468
1300
|
*/
|
|
1469
1301
|
function update(data) {
|
|
1470
1302
|
// First, validate the Onyx object is in the format we expect
|
|
1471
|
-
|
|
1472
|
-
if (!
|
|
1303
|
+
underscore_1.default.each(data, ({ onyxMethod, key, value }) => {
|
|
1304
|
+
if (!underscore_1.default.contains([METHOD.CLEAR, METHOD.SET, METHOD.MERGE, METHOD.MERGE_COLLECTION, METHOD.MULTI_SET], onyxMethod)) {
|
|
1473
1305
|
throw new Error(`Invalid onyxMethod ${onyxMethod} in Onyx update.`);
|
|
1474
1306
|
}
|
|
1475
1307
|
if (onyxMethod === METHOD.MULTI_SET) {
|
|
1476
1308
|
// For multiset, we just expect the value to be an object
|
|
1477
|
-
if (!
|
|
1309
|
+
if (!underscore_1.default.isObject(value) || underscore_1.default.isArray(value) || underscore_1.default.isFunction(value)) {
|
|
1478
1310
|
throw new Error('Invalid value provided in Onyx multiSet. Onyx multiSet value must be of type object.');
|
|
1479
1311
|
}
|
|
1480
|
-
}
|
|
1312
|
+
}
|
|
1313
|
+
else if (onyxMethod !== METHOD.CLEAR && !underscore_1.default.isString(key)) {
|
|
1481
1314
|
throw new Error(`Invalid ${typeof key} key provided in Onyx update. Onyx key must be of type string.`);
|
|
1482
1315
|
}
|
|
1483
1316
|
});
|
|
1484
|
-
|
|
1485
1317
|
const promises = [];
|
|
1486
1318
|
let clearPromise = Promise.resolve();
|
|
1487
|
-
|
|
1488
|
-
_.each(data, ({onyxMethod, key, value}) => {
|
|
1319
|
+
underscore_1.default.each(data, ({ onyxMethod, key, value }) => {
|
|
1489
1320
|
switch (onyxMethod) {
|
|
1490
1321
|
case METHOD.SET:
|
|
1491
1322
|
promises.push(() => set(key, value));
|
|
@@ -1506,21 +1337,17 @@ function update(data) {
|
|
|
1506
1337
|
break;
|
|
1507
1338
|
}
|
|
1508
1339
|
});
|
|
1509
|
-
|
|
1510
|
-
return clearPromise.then(() => Promise.all(_.map(promises, (p) => p())));
|
|
1340
|
+
return clearPromise.then(() => Promise.all(underscore_1.default.map(promises, (p) => p())));
|
|
1511
1341
|
}
|
|
1512
|
-
|
|
1513
1342
|
/**
|
|
1514
1343
|
* When set these keys will not be persisted to storage
|
|
1515
1344
|
* @param {string[]} keyList
|
|
1516
1345
|
*/
|
|
1517
1346
|
function setMemoryOnlyKeys(keyList) {
|
|
1518
|
-
|
|
1519
|
-
|
|
1347
|
+
storage_1.default.setMemoryOnlyKeys(keyList);
|
|
1520
1348
|
// When in memory only mode for certain keys we do not want to ever drop items from the cache as the user will have no way to recover them again via storage.
|
|
1521
|
-
|
|
1349
|
+
OnyxCache_1.default.setRecentKeysLimit(Infinity);
|
|
1522
1350
|
}
|
|
1523
|
-
|
|
1524
1351
|
/**
|
|
1525
1352
|
* Initialize the store with actions and listening for storage events
|
|
1526
1353
|
*
|
|
@@ -1546,60 +1373,39 @@ function setMemoryOnlyKeys(keyList) {
|
|
|
1546
1373
|
* },
|
|
1547
1374
|
* });
|
|
1548
1375
|
*/
|
|
1549
|
-
function init({
|
|
1550
|
-
keys = {},
|
|
1551
|
-
initialKeyStates = {},
|
|
1552
|
-
safeEvictionKeys = [],
|
|
1553
|
-
maxCachedKeysCount = 1000,
|
|
1554
|
-
captureMetrics = false,
|
|
1555
|
-
shouldSyncMultipleInstances = Boolean(global.localStorage),
|
|
1556
|
-
debugSetState = false,
|
|
1557
|
-
} = {}) {
|
|
1376
|
+
function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCachedKeysCount = 1000, captureMetrics = false, shouldSyncMultipleInstances = Boolean(global.localStorage), debugSetState = false, } = {}) {
|
|
1558
1377
|
if (captureMetrics) {
|
|
1559
1378
|
// The code here is only bundled and applied when the captureMetrics is set
|
|
1560
1379
|
// eslint-disable-next-line no-use-before-define
|
|
1561
1380
|
applyDecorators();
|
|
1562
1381
|
}
|
|
1563
|
-
|
|
1564
1382
|
if (debugSetState) {
|
|
1565
1383
|
PerformanceUtils.setShouldDebugSetState(true);
|
|
1566
1384
|
}
|
|
1567
|
-
|
|
1568
1385
|
if (maxCachedKeysCount > 0) {
|
|
1569
|
-
|
|
1386
|
+
OnyxCache_1.default.setRecentKeysLimit(maxCachedKeysCount);
|
|
1570
1387
|
}
|
|
1571
|
-
|
|
1572
1388
|
// We need the value of the collection keys later for checking if a
|
|
1573
1389
|
// key is a collection. We store it in a map for faster lookup.
|
|
1574
|
-
const collectionValues =
|
|
1575
|
-
onyxCollectionKeyMap =
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
return acc;
|
|
1580
|
-
},
|
|
1581
|
-
new Map(),
|
|
1582
|
-
);
|
|
1583
|
-
|
|
1390
|
+
const collectionValues = underscore_1.default.values(keys.COLLECTION);
|
|
1391
|
+
onyxCollectionKeyMap = underscore_1.default.reduce(collectionValues, (acc, val) => {
|
|
1392
|
+
acc.set(val, true);
|
|
1393
|
+
return acc;
|
|
1394
|
+
}, new Map());
|
|
1584
1395
|
// Set our default key states to use when initializing and clearing Onyx data
|
|
1585
1396
|
defaultKeyStates = initialKeyStates;
|
|
1586
|
-
|
|
1587
|
-
DevTools.initState(initialKeyStates);
|
|
1588
|
-
|
|
1397
|
+
DevTools_1.default.initState(initialKeyStates);
|
|
1589
1398
|
// Let Onyx know about which keys are safe to evict
|
|
1590
1399
|
evictionAllowList = safeEvictionKeys;
|
|
1591
|
-
|
|
1592
1400
|
// Initialize all of our keys with data provided then give green light to any pending connections
|
|
1593
1401
|
Promise.all([addAllSafeEvictionKeysToRecentlyAccessedList(), initializeWithDefaultKeyStates()]).then(deferredInitTask.resolve);
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
cache.set(key, value);
|
|
1402
|
+
if (shouldSyncMultipleInstances && underscore_1.default.isFunction(storage_1.default.keepInstancesSync)) {
|
|
1403
|
+
storage_1.default.keepInstancesSync((key, value) => {
|
|
1404
|
+
OnyxCache_1.default.set(key, value);
|
|
1598
1405
|
keyChanged(key, value);
|
|
1599
1406
|
});
|
|
1600
1407
|
}
|
|
1601
1408
|
}
|
|
1602
|
-
|
|
1603
1409
|
const Onyx = {
|
|
1604
1410
|
connect,
|
|
1605
1411
|
disconnect,
|
|
@@ -1620,7 +1426,6 @@ const Onyx = {
|
|
|
1620
1426
|
tryGetCachedValue,
|
|
1621
1427
|
hasPendingMergeForKey,
|
|
1622
1428
|
};
|
|
1623
|
-
|
|
1624
1429
|
/**
|
|
1625
1430
|
* Apply calls statistic decorators to benchmark Onyx
|
|
1626
1431
|
*
|
|
@@ -1629,7 +1434,6 @@ const Onyx = {
|
|
|
1629
1434
|
function applyDecorators() {
|
|
1630
1435
|
// We're requiring the script dynamically here so that it's only evaluated when decorators are used
|
|
1631
1436
|
const decorate = require('./metrics');
|
|
1632
|
-
|
|
1633
1437
|
// Re-assign with decorated functions
|
|
1634
1438
|
/* eslint-disable no-func-assign */
|
|
1635
1439
|
get = decorate.decorateWithMetrics(get, 'Onyx:get');
|
|
@@ -1642,7 +1446,6 @@ function applyDecorators() {
|
|
|
1642
1446
|
initializeWithDefaultKeyStates = decorate.decorateWithMetrics(initializeWithDefaultKeyStates, 'Onyx:defaults');
|
|
1643
1447
|
update = decorate.decorateWithMetrics(update, 'Onyx:update');
|
|
1644
1448
|
/* eslint-enable */
|
|
1645
|
-
|
|
1646
1449
|
// Re-expose decorated methods
|
|
1647
1450
|
/* eslint-disable rulesdir/prefer-actions-set-data */
|
|
1648
1451
|
Onyx.set = set;
|
|
@@ -1652,11 +1455,9 @@ function applyDecorators() {
|
|
|
1652
1455
|
Onyx.mergeCollection = mergeCollection;
|
|
1653
1456
|
Onyx.update = update;
|
|
1654
1457
|
/* eslint-enable */
|
|
1655
|
-
|
|
1656
1458
|
// Expose stats methods on Onyx
|
|
1657
1459
|
Onyx.getMetrics = decorate.getMetrics;
|
|
1658
1460
|
Onyx.resetMetrics = decorate.resetMetrics;
|
|
1659
1461
|
Onyx.printMetrics = decorate.printMetrics;
|
|
1660
1462
|
}
|
|
1661
|
-
|
|
1662
|
-
export default Onyx;
|
|
1463
|
+
exports.default = Onyx;
|