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