@airtable/blocks 1.11.1-experimental-68738f2-20220526 → 1.13.0

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.
Files changed (115) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/error_utils.js +1 -19
  3. package/dist/cjs/global_config.js +3 -3
  4. package/dist/cjs/injected/airtable_interface.js +1 -1
  5. package/dist/cjs/models/base.js +9 -9
  6. package/dist/cjs/models/cursor.js +3 -3
  7. package/dist/cjs/models/field.js +3 -3
  8. package/dist/cjs/models/grouped_record_query_result.js +14 -5
  9. package/dist/cjs/models/linked_records_query_result.js +27 -78
  10. package/dist/cjs/models/mutations.js +163 -18
  11. package/dist/cjs/models/record.js +55 -310
  12. package/dist/cjs/models/record_query_result.js +2 -5
  13. package/dist/cjs/models/record_store.js +829 -555
  14. package/dist/cjs/models/session.js +3 -3
  15. package/dist/cjs/models/table.js +31 -31
  16. package/dist/cjs/models/table_or_view_query_result.js +419 -526
  17. package/dist/cjs/models/view.js +23 -1
  18. package/dist/cjs/models/view_data_store.js +295 -255
  19. package/dist/cjs/private_utils.js +0 -40
  20. package/dist/cjs/sdk.js +9 -12
  21. package/dist/cjs/settings_button.js +1 -1
  22. package/dist/cjs/testing/abstract_mock_airtable_interface.js +11 -57
  23. package/dist/cjs/types/airtable_interface.js +2 -17
  24. package/dist/cjs/ui/block_wrapper.js +2 -5
  25. package/dist/cjs/ui/expand_record_picker_async.js +1 -1
  26. package/dist/cjs/ui/global_config_synced_component_helpers.js +6 -0
  27. package/dist/cjs/ui/initialize_block.js +3 -3
  28. package/dist/cjs/ui/use_global_config.js +1 -1
  29. package/dist/cjs/ui/use_loadable.js +3 -3
  30. package/dist/cjs/ui/use_record_action_data.js +11 -11
  31. package/dist/cjs/ui/use_records.js +1 -5
  32. package/dist/cjs/ui/use_settings_button.js +1 -1
  33. package/dist/cjs/ui/viewport_constraint.js +1 -1
  34. package/dist/cjs/unstable_testing_utils.js +1 -61
  35. package/dist/cjs/viewport.js +9 -9
  36. package/dist/cjs/watchable.js +1 -15
  37. package/dist/types/src/error_utils.d.ts +1 -2
  38. package/dist/types/src/error_utils.d.ts.map +1 -1
  39. package/dist/types/src/global_config.d.ts +3 -3
  40. package/dist/types/src/models/base.d.ts +9 -9
  41. package/dist/types/src/models/cursor.d.ts +3 -3
  42. package/dist/types/src/models/field.d.ts +3 -3
  43. package/dist/types/src/models/grouped_record_query_result.d.ts +3 -3
  44. package/dist/types/src/models/grouped_record_query_result.d.ts.map +1 -1
  45. package/dist/types/src/models/linked_records_query_result.d.ts.map +1 -1
  46. package/dist/types/src/models/mutations.d.ts.map +1 -1
  47. package/dist/types/src/models/record.d.ts +3 -12
  48. package/dist/types/src/models/record.d.ts.map +1 -1
  49. package/dist/types/src/models/record_query_result.d.ts +4 -5
  50. package/dist/types/src/models/record_query_result.d.ts.map +1 -1
  51. package/dist/types/src/models/record_store.d.ts.map +1 -1
  52. package/dist/types/src/models/session.d.ts +3 -3
  53. package/dist/types/src/models/table.d.ts +25 -25
  54. package/dist/types/src/models/table_or_view_query_result.d.ts +5 -3
  55. package/dist/types/src/models/table_or_view_query_result.d.ts.map +1 -1
  56. package/dist/types/src/models/view.d.ts +11 -0
  57. package/dist/types/src/models/view.d.ts.map +1 -1
  58. package/dist/types/src/models/view_data_store.d.ts +1 -0
  59. package/dist/types/src/models/view_data_store.d.ts.map +1 -1
  60. package/dist/types/src/models/view_metadata_query_result.d.ts +1 -1
  61. package/dist/types/src/models/view_metadata_query_result.d.ts.map +1 -1
  62. package/dist/types/src/private_utils.d.ts +1 -24
  63. package/dist/types/src/private_utils.d.ts.map +1 -1
  64. package/dist/types/src/sdk.d.ts +3 -1
  65. package/dist/types/src/sdk.d.ts.map +1 -1
  66. package/dist/types/src/settings_button.d.ts +1 -1
  67. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts +11 -11
  68. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts.map +1 -1
  69. package/dist/types/src/types/airtable_interface.d.ts +20 -59
  70. package/dist/types/src/types/airtable_interface.d.ts.map +1 -1
  71. package/dist/types/src/types/field.d.ts +10 -5
  72. package/dist/types/src/types/field.d.ts.map +1 -1
  73. package/dist/types/src/types/table.d.ts +2 -0
  74. package/dist/types/src/types/table.d.ts.map +1 -1
  75. package/dist/types/src/types/view.d.ts +9 -3
  76. package/dist/types/src/types/view.d.ts.map +1 -1
  77. package/dist/types/src/ui/block_wrapper.d.ts.map +1 -1
  78. package/dist/types/src/ui/expand_record_picker_async.d.ts +1 -1
  79. package/dist/types/src/ui/global_config_synced_component_helpers.d.ts.map +1 -1
  80. package/dist/types/src/ui/initialize_block.d.ts +1 -1
  81. package/dist/types/src/ui/record_card.d.ts +1 -1
  82. package/dist/types/src/ui/use_global_config.d.ts +1 -1
  83. package/dist/types/src/ui/use_loadable.d.ts +2 -2
  84. package/dist/types/src/ui/use_record_action_data.d.ts +11 -11
  85. package/dist/types/src/ui/use_settings_button.d.ts +1 -1
  86. package/dist/types/src/ui/viewport_constraint.d.ts +3 -3
  87. package/dist/types/src/ui/viewport_constraint.d.ts.map +1 -1
  88. package/dist/types/src/unstable_testing_utils.d.ts +1 -4
  89. package/dist/types/src/unstable_testing_utils.d.ts.map +1 -1
  90. package/dist/types/src/viewport.d.ts +9 -9
  91. package/dist/types/src/watchable.d.ts.map +1 -1
  92. package/dist/types/{src/testing → test/airtable_interface_mocks}/fixture_data.d.ts +25 -44
  93. package/dist/types/test/airtable_interface_mocks/fixture_data.d.ts.map +1 -0
  94. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts +1 -1
  95. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts.map +1 -1
  96. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts +12 -19
  97. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts.map +1 -1
  98. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts +1 -1
  99. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts.map +1 -1
  100. package/dist/types/test/test_helpers.d.ts +0 -2
  101. package/dist/types/test/test_helpers.d.ts.map +1 -1
  102. package/package.json +2 -3
  103. package/dist/cjs/models/query_manager.js +0 -328
  104. package/dist/cjs/testing/fixture_data.js +0 -271
  105. package/dist/cjs/testing/mock_base_data_stores.js +0 -891
  106. package/dist/cjs/types/block_query_spec.js +0 -85
  107. package/dist/types/src/models/query_manager.d.ts +0 -2
  108. package/dist/types/src/models/query_manager.d.ts.map +0 -1
  109. package/dist/types/src/testing/fixture_data.d.ts.map +0 -1
  110. package/dist/types/src/testing/mock_base_data_stores.d.ts +0 -59
  111. package/dist/types/src/testing/mock_base_data_stores.d.ts.map +0 -1
  112. package/dist/types/src/types/block_query_spec.d.ts +0 -139
  113. package/dist/types/src/types/block_query_spec.d.ts.map +0 -1
  114. package/dist/types/test/testing/fixture_data.test.d.ts +0 -2
  115. package/dist/types/test/testing/fixture_data.test.d.ts.map +0 -1
@@ -6,26 +6,20 @@ require("core-js/modules/es.symbol");
6
6
 
7
7
  require("core-js/modules/es.symbol.description");
8
8
 
9
- require("core-js/modules/es.array.concat");
10
-
11
9
  require("core-js/modules/es.array.filter");
12
10
 
13
11
  require("core-js/modules/es.array.iterator");
14
12
 
15
13
  require("core-js/modules/es.array.map");
16
14
 
17
- require("core-js/modules/es.object.get-own-property-descriptors");
15
+ require("core-js/modules/es.object.entries");
18
16
 
19
17
  require("core-js/modules/es.object.to-string");
20
18
 
21
19
  require("core-js/modules/es.promise");
22
20
 
23
- require("core-js/modules/es.set");
24
-
25
21
  require("core-js/modules/es.string.starts-with");
26
22
 
27
- require("core-js/modules/web.dom-collections.for-each");
28
-
29
23
  require("core-js/modules/web.dom-collections.iterator");
30
24
 
31
25
  Object.defineProperty(exports, "__esModule", {
@@ -33,6 +27,8 @@ Object.defineProperty(exports, "__esModule", {
33
27
  });
34
28
  exports.default = exports.WatchableRecordStoreKeys = void 0;
35
29
 
30
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
31
+
36
32
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
37
33
 
38
34
  require("regenerator-runtime/runtime");
@@ -41,9 +37,11 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
41
37
 
42
38
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
43
39
 
40
+ var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
41
+
44
42
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
45
43
 
46
- var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
44
+ var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
47
45
 
48
46
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
49
47
 
@@ -55,20 +53,12 @@ var _private_utils = require("../private_utils");
55
53
 
56
54
  var _error_utils = require("../error_utils");
57
55
 
58
- var _warning = _interopRequireDefault(require("../warning"));
59
-
60
- var _block_query_spec = require("../types/block_query_spec");
61
-
62
56
  var _abstract_model_with_async_data = _interopRequireDefault(require("./abstract_model_with_async_data"));
63
57
 
64
58
  var _record = _interopRequireDefault(require("./record"));
65
59
 
66
60
  var _view_data_store = _interopRequireDefault(require("./view_data_store"));
67
61
 
68
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
69
-
70
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
71
-
72
62
  var WatchableRecordStoreKeys = Object.freeze({
73
63
  records: 'records',
74
64
  recordIds: 'recordIds',
@@ -83,9 +73,8 @@ var WatchableCellValuesInFieldKeyPrefix = 'cellValuesInField:';
83
73
  */
84
74
 
85
75
  /**
86
- * One RecordStore exists per table, and can access *some* of the record data associated with that
87
- * table, depending on which records are actually required for the slices of data that are subscribed.
88
- * Table itself is for schema information only, so isn't the appropriate place for record data.
76
+ * One RecordStore exists per table, and contains all the record data associated with that table.
77
+ * Table itself is for schema information only, so isn't the appropriate place for this data.
89
78
  *
90
79
  * @internal
91
80
  */
@@ -98,6 +87,16 @@ function (_AbstractModelWithAsy) {
98
87
  value: function _isWatchableKey(key) {
99
88
  return (0, _private_utils.isEnumValue)(WatchableRecordStoreKeys, key) || key.startsWith(WatchableCellValuesInFieldKeyPrefix);
100
89
  }
90
+ }, {
91
+ key: "_shouldLoadDataForKey",
92
+ value: function _shouldLoadDataForKey(key) {
93
+ // "Data" means *all* cell values in the table. If only watching records/recordIds,
94
+ // we'll just load record metadata (id, createdTime, commentCount).
95
+ // If only watching specific fields, we'll just load cell values in those
96
+ // fields. Both of those scenarios are handled manually by this class,
97
+ // instead of relying on AbstractModelWithAsyncData.
98
+ return key === WatchableRecordStoreKeys.cellValues;
99
+ }
101
100
  }]);
102
101
 
103
102
  function RecordStore(sdk, tableId) {
@@ -109,15 +108,12 @@ function (_AbstractModelWithAsy) {
109
108
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_recordModelsById", {});
110
109
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_primaryFieldId", void 0);
111
110
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_airtableInterface", void 0);
112
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_queryManager", void 0);
113
111
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_viewDataStoresByViewId", {});
114
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_airtableRecordStoreForTable", void 0);
115
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_numQueriesSubscribedToAllRecords", 0);
116
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_queryIdFromReferenceCountedLoadData", null);
117
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_loadedQuerySpecIds", new Set());
118
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_forceUnloadCallbacks", []);
112
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_areCellValuesLoadedByFieldId", {});
113
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_pendingCellValuesLoadPromiseByFieldId", {});
114
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_cellValuesRetainCountByFieldId", {});
115
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_timeoutForRemovingFieldIds", null);
119
116
  _this._airtableInterface = sdk.__airtableInterface;
120
- _this._queryManager = sdk._queryManager;
121
117
  _this.tableId = tableId; // A bit of a hack, but we use the primary field ID to load record
122
118
  // metadata (see _getFieldIdForCausingRecordMetadataToLoad). We copy the
123
119
  // ID here instead of calling this.primaryField.id since that would crash
@@ -128,323 +124,96 @@ function (_AbstractModelWithAsy) {
128
124
  }
129
125
 
130
126
  (0, _createClass2.default)(RecordStore, [{
131
- key: "_getRecordStoreForTable",
132
- value: function _getRecordStoreForTable() {
133
- if (!this._airtableRecordStoreForTable) {
134
- var airtableRecordStoreForTable = this._airtableInterface.getTableRecordStoreIfExists(this.tableId);
135
-
136
- (0, _error_utils.invariant)(airtableRecordStoreForTable, 'no RecordStoreForTable found for tableId %s', this.tableId);
137
- this._airtableRecordStoreForTable = airtableRecordStoreForTable;
138
- }
139
-
140
- return this._airtableRecordStoreForTable;
141
- }
142
- }, {
143
127
  key: "getViewDataStore",
144
128
  value: function getViewDataStore(viewId) {
145
129
  if (this._viewDataStoresByViewId[viewId]) {
146
130
  return this._viewDataStoresByViewId[viewId];
147
131
  }
148
132
 
149
- (0, _error_utils.invariant)(this._data.viewsById[viewId], 'view must exist'); // TODO: (#proj-blocks-sdk-record-limits) Rename ViewDataStore so that it does not
150
- // conflict with hyperbase ViewDataStore. Here we are using the SDK ViewDataStore
151
- // which is a thin wrapper around the hyperbase one.
152
-
133
+ (0, _error_utils.invariant)(this._data.viewsById[viewId], 'view must exist');
153
134
  var viewDataStore = new _view_data_store.default(this._sdk, this, viewId);
154
135
  this._viewDataStoresByViewId[viewId] = viewDataStore;
155
136
  return viewDataStore;
156
137
  }
157
138
  }, {
158
- key: "sendViewDataDeletionIfViewIsDeleted",
159
- value: function sendViewDataDeletionIfViewIsDeleted(viewId) {
160
- var viewDataStoreIfExists = this._viewDataStoresByViewId[viewId];
161
-
162
- if (viewDataStoreIfExists && viewDataStoreIfExists.isDeleted) {
163
- viewDataStoreIfExists.__onDataDeletion();
164
-
165
- delete this._viewDataStoresByViewId[viewId];
166
- }
167
- }
168
- }, {
169
- key: "_findMatchingBlockSubscriptionChanges",
170
- value: function _findMatchingBlockSubscriptionChanges(changes, changeTypeToMatch) {
171
- return changes.filter(change => change.type === changeTypeToMatch && change.tableId === this.tableId);
172
- }
173
- }, {
174
- key: "_constructBlockQuerySpecAndCallbackForWatchableKeyWithRecordQueryResult",
175
- value: function _constructBlockQuerySpecAndCallbackForWatchableKeyWithRecordQueryResult(recordQueryResult, key, callbackForRegistration, context) {
176
- var fieldSelectionDefinition;
177
- var originThis = recordQueryResult;
178
- var onChangeCallback;
179
-
180
- switch (key) {
181
- case WatchableRecordStoreKeys.records:
182
- case WatchableRecordStoreKeys.recordIds:
183
- fieldSelectionDefinition = {
184
- type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
185
- fieldIds: []
186
- };
187
-
188
- onChangeCallback = changes => {
189
- var addedRecordsChanges = this._findMatchingBlockSubscriptionChanges(changes, _block_query_spec.BlockQuerySubscriptionChangeType.ADDED_RECORD_IDS);
190
-
191
- var removedRecordsChanges = this._findMatchingBlockSubscriptionChanges(changes, _block_query_spec.BlockQuerySubscriptionChangeType.REMOVED_RECORD_IDS);
192
-
193
- var addedRecordIds = [];
194
- var removedRecordIds = [];
195
- var _iteratorNormalCompletion = true;
196
- var _didIteratorError = false;
197
- var _iteratorError = undefined;
198
-
199
- try {
200
- for (var _iterator = addedRecordsChanges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
201
- var addedRecordsChange = _step.value;
202
- (0, _error_utils.invariant)(addedRecordsChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.ADDED_RECORD_IDS, 'Added Records change is not a supported type');
203
- addedRecordIds = addedRecordIds.concat(addedRecordsChange.recordIds);
204
- }
205
- } catch (err) {
206
- _didIteratorError = true;
207
- _iteratorError = err;
208
- } finally {
209
- try {
210
- if (!_iteratorNormalCompletion && _iterator.return != null) {
211
- _iterator.return();
212
- }
213
- } finally {
214
- if (_didIteratorError) {
215
- throw _iteratorError;
216
- }
217
- }
218
- }
219
-
220
- var _iteratorNormalCompletion2 = true;
221
- var _didIteratorError2 = false;
222
- var _iteratorError2 = undefined;
223
-
224
- try {
225
- for (var _iterator2 = removedRecordsChanges[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
226
- var removedRecordsChange = _step2.value;
227
- (0, _error_utils.invariant)(removedRecordsChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.REMOVED_RECORD_IDS, 'Removed Records change is not a supported type');
228
- removedRecordIds = removedRecordIds.concat(removedRecordsChange.recordIds);
229
- }
230
- } catch (err) {
231
- _didIteratorError2 = true;
232
- _iteratorError2 = err;
233
- } finally {
234
- try {
235
- if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
236
- _iterator2.return();
237
- }
238
- } finally {
239
- if (_didIteratorError2) {
240
- throw _iteratorError2;
241
- }
242
- }
243
- }
244
-
245
- var recordIdsChanged = {
246
- addedRecordIds,
247
- removedRecordIds
248
- };
249
- callbackForRegistration.call(context, originThis, key, recordIdsChanged);
250
- };
251
-
252
- break;
253
-
254
- case WatchableRecordStoreKeys.cellValues:
255
- fieldSelectionDefinition = {
256
- type: _block_query_spec.BlockFieldSelectionSpecType.ALL_FIELDS_FROM_TABLE
257
- };
258
-
259
- onChangeCallback = changes => {
260
- var modifiedCellValuesChanges = this._findMatchingBlockSubscriptionChanges(changes, _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE);
261
-
262
- if (modifiedCellValuesChanges.length === 0) {
263
- callbackForRegistration.call(context, originThis, key);
264
- return;
265
- }
266
-
267
- var recordsAndFieldsChanged = {
268
- recordIds: [],
269
- fieldIds: []
270
- };
271
- var _iteratorNormalCompletion3 = true;
272
- var _didIteratorError3 = false;
273
- var _iteratorError3 = undefined;
274
-
275
- try {
276
- for (var _iterator3 = modifiedCellValuesChanges[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
277
- var modifiedCellValuesChange = _step3.value;
278
- (0, _error_utils.invariant)(modifiedCellValuesChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE, 'Modified cell value change is not a supported type');
279
- var fieldIdsByRecordId = modifiedCellValuesChange.fieldIdsByRecordId,
280
- fieldIds = modifiedCellValuesChange.fieldIds;
281
- recordsAndFieldsChanged.recordIds = recordsAndFieldsChanged.recordIds.concat(Object.keys(fieldIdsByRecordId));
282
- recordsAndFieldsChanged.fieldIds = recordsAndFieldsChanged.fieldIds.concat(fieldIds);
283
- }
284
- } catch (err) {
285
- _didIteratorError3 = true;
286
- _iteratorError3 = err;
287
- } finally {
288
- try {
289
- if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
290
- _iterator3.return();
291
- }
292
- } finally {
293
- if (_didIteratorError3) {
294
- throw _iteratorError3;
295
- }
296
- }
297
- }
298
-
299
- callbackForRegistration.call(context, originThis, key, recordsAndFieldsChanged);
300
- };
301
-
302
- break;
303
-
304
- default:
305
- {
306
- // Since this watch class is only called internally from within the SDK
307
- // an error here is an internal SDK error.
308
- (0, _error_utils.invariant)(key.startsWith(WatchableCellValuesInFieldKeyPrefix), 'Internal error - Unrecognized watch key on record_store: %s', key);
309
- var fieldId = key.substring(WatchableCellValuesInFieldKeyPrefix.length); // Assert that fieldId is validly formatted, not that it is a valid fieldId in this table
310
-
311
- (0, _error_utils.invariant)((0, _private_utils.isFieldId)(fieldId), 'fieldId passed as watchable fieldId is not a valid format' + fieldId);
312
- fieldSelectionDefinition = {
313
- type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
314
- fieldIds: [fieldId]
315
- };
316
-
317
- onChangeCallback = changes => {
318
- var modifiedCellValuesChanges = this._findMatchingBlockSubscriptionChanges(changes, _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE);
319
-
320
- if (modifiedCellValuesChanges.length === 0) {
321
- callbackForRegistration.call(context, originThis, key);
322
- return;
323
- }
324
-
325
- var recordsChanged = [];
326
- var _iteratorNormalCompletion4 = true;
327
- var _didIteratorError4 = false;
328
- var _iteratorError4 = undefined;
329
-
330
- try {
331
- for (var _iterator4 = modifiedCellValuesChanges[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
332
- var modifiedCellValuesChange = _step4.value;
333
- (0, _error_utils.invariant)(modifiedCellValuesChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE, 'Modified cell value change is not a supported type');
334
- recordsChanged = recordsChanged.concat(Object.keys(modifiedCellValuesChange.fieldIdsByRecordId));
335
- } // Why is this callback format different than when watching just cellValues?
336
- // It's because in the past this was the format we used: https://github.com/Hyperbase/blocks-sdk/blob/32b6c59944f0d89d2c0b3b61b7db615145a8856b/packages/sdk/src/models/record_store.ts#L547-L554
337
-
338
- } catch (err) {
339
- _didIteratorError4 = true;
340
- _iteratorError4 = err;
341
- } finally {
342
- try {
343
- if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
344
- _iterator4.return();
345
- }
346
- } finally {
347
- if (_didIteratorError4) {
348
- throw _iteratorError4;
349
- }
350
- }
351
- }
139
+ key: "watch",
140
+ value: function watch(keys, callback, context) {
141
+ var validKeys = (0, _get2.default)((0, _getPrototypeOf2.default)(RecordStore.prototype), "watch", this).call(this, keys, callback, context);
352
142
 
353
- callbackForRegistration.call(context, originThis, key, recordsChanged, fieldId);
354
- };
143
+ var fieldIdsToLoad = this._getFieldIdsToLoadFromWatchableKeys(validKeys);
355
144
 
356
- break;
357
- }
145
+ if (fieldIdsToLoad.length > 0) {
146
+ (0, _private_utils.fireAndForgetPromise)(this.loadCellValuesInFieldIdsAsync.bind(this, fieldIdsToLoad));
358
147
  }
359
148
 
360
- return {
361
- querySpecToSubscribeWith: recordQueryResult.__constructQuerySpecForBlockFieldSelectionSpec(fieldSelectionDefinition),
362
- onChangeCallback
363
- };
364
- }
365
- }, {
366
- key: "watch",
367
- value: function watch(keys, callbackForRegistration, context) {
368
- throw (0, _error_utils.spawnError)('Must not call watch on record_store directly');
369
- }
370
- }, {
371
- key: "watchWithRecordQueryResult",
372
- value: function watchWithRecordQueryResult(_ref) {
373
- var priority = _ref.priority,
374
- recordQueryResult = _ref.recordQueryResult,
375
- keys = _ref.keys,
376
- callbackForRegistration = _ref.callbackForRegistration,
377
- context = _ref.context;
378
-
379
- // Note that we do not call super.watch(), we use the
380
- // alternative this._queryManager.watchWithQuerySpec
381
- this._assertNotForceUnloaded();
382
-
383
- var validKeys = this._getWatchableValidKeysOrThrow(keys, 'table watch');
384
-
385
- validKeys.forEach(validKey => {
386
- var _this$_constructBlock = this._constructBlockQuerySpecAndCallbackForWatchableKeyWithRecordQueryResult(recordQueryResult, validKey, callbackForRegistration, context),
387
- querySpecToSubscribeWith = _this$_constructBlock.querySpecToSubscribeWith,
388
- onChangeCallback = _this$_constructBlock.onChangeCallback;
389
-
390
- var onChangeCallbackThatIncrementsWatchChangeCount = args => {
391
- // Normally calling __incrementOnChangeCount is handled by queryManager.watchWithQuerySpec
392
- // however it will only increment the change count on the recordStore. In this case we ALSO
393
- // need to increment the change count on the recordQueryResult.
394
- recordQueryResult.__incrementOnChangeCount();
395
-
396
- onChangeCallback(args);
397
- };
398
-
399
- this._queryManager.watchWithQuerySpec(priority, querySpecToSubscribeWith, this, validKey, onChangeCallbackThatIncrementsWatchChangeCount, callbackForRegistration, context);
400
- });
401
149
  return validKeys;
402
150
  }
403
151
  }, {
404
152
  key: "unwatch",
405
- value: function unwatch(keys, callbackForRegistration, context) {
406
- // We have already unsubscribed from all watches (because query-based watches force data to be loaded)
407
- // so no watches are subscribed
408
- if (this._isForceUnloaded) {
409
- return [];
410
- } // We warn instead of throw here because we used to warn instead of throw and don't
411
- // want to make a breaking change.
153
+ value: function unwatch(keys, callback, context) {
154
+ var validKeys = (0, _get2.default)((0, _getPrototypeOf2.default)(RecordStore.prototype), "unwatch", this).call(this, keys, callback, context);
412
155
 
156
+ var fieldIdsToUnload = this._getFieldIdsToLoadFromWatchableKeys(validKeys);
413
157
 
414
- var validKeys = this._getWatchableValidKeysOrThrow(keys, 'table unwatch', true);
158
+ if (fieldIdsToUnload.length > 0) {
159
+ this.unloadCellValuesInFieldIds(fieldIdsToUnload);
160
+ }
415
161
 
416
- validKeys.forEach(key => this._queryManager.unwatchFromQueryKey(key, callbackForRegistration, context));
417
162
  return validKeys;
418
163
  }
419
164
  }, {
420
- key: "isRecordDeleted",
421
- value: function isRecordDeleted(recordId) {
422
- return this._getRecordStoreForTable().isRecordDeleted(recordId);
423
- } // Maybe add a comment "pending removal of inheriting from parent class AbstractModelWithAsyncData"
424
- // istanbul ignore next
165
+ key: "_getFieldIdsToLoadFromWatchableKeys",
166
+ value: function _getFieldIdsToLoadFromWatchableKeys(keys) {
167
+ var fieldIdsToLoad = [];
168
+ var _iteratorNormalCompletion = true;
169
+ var _didIteratorError = false;
170
+ var _iteratorError = undefined;
425
171
 
172
+ try {
173
+ for (var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
174
+ var key = _step.value;
175
+
176
+ if (key.startsWith(WatchableCellValuesInFieldKeyPrefix)) {
177
+ var fieldId = key.substring(WatchableCellValuesInFieldKeyPrefix.length);
178
+ fieldIdsToLoad.push(fieldId);
179
+ } else if (key === WatchableRecordStoreKeys.records || key === WatchableRecordStoreKeys.recordIds) {
180
+ fieldIdsToLoad.push(this._getFieldIdForCausingRecordMetadataToLoad());
181
+ }
182
+ }
183
+ } catch (err) {
184
+ _didIteratorError = true;
185
+ _iteratorError = err;
186
+ } finally {
187
+ try {
188
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
189
+ _iterator.return();
190
+ }
191
+ } finally {
192
+ if (_didIteratorError) {
193
+ throw _iteratorError;
194
+ }
195
+ }
196
+ }
197
+
198
+ return fieldIdsToLoad;
199
+ }
426
200
  }, {
427
201
  key: "_onChangeIsDataLoaded",
428
- value: function _onChangeIsDataLoaded() {
429
- throw (0, _error_utils.spawnError)('internal error - onChangeIsDataLoaded illegal to call on record_store');
430
- }
202
+ value: function _onChangeIsDataLoaded() {} // noop
203
+
431
204
  /**
432
205
  * The records in this table. The order is arbitrary since records are
433
206
  * only ordered in the context of a specific view.
434
207
  */
435
208
 
436
- }, {
437
- key: "isRecordLoaded",
438
- value: function isRecordLoaded(recordId) {
439
- return this._getRecordStoreForTable().isRecordPartiallyLoaded(recordId);
440
- }
441
209
  }, {
442
210
  key: "getRecordByIdIfExists",
443
211
  value: function getRecordByIdIfExists(recordId) {
444
- (0, _error_utils.invariant)(this._loadedQuerySpecIds.size > 0, 'Record metadata is not loaded');
212
+ var recordsById = this._data.recordsById;
213
+ (0, _error_utils.invariant)(recordsById, 'Record metadata is not loaded');
445
214
  (0, _error_utils.invariant)(typeof recordId === 'string', 'getRecordById expects a string');
446
215
 
447
- if (!this._getRecordStoreForTable().isRecordPartiallyLoaded(recordId)) {
216
+ if (!recordsById[recordId]) {
448
217
  return null;
449
218
  } else {
450
219
  if (this._recordModelsById[recordId]) {
@@ -459,64 +228,76 @@ function (_AbstractModelWithAsy) {
459
228
  }, {
460
229
  key: "__onDataDeletion",
461
230
  value: function __onDataDeletion() {
462
- this._forceUnload();
231
+ // also need to call unloadCellValuesInFieldIds because otherwise
232
+ // on the hyperbase side, the old record store would still be subscribed
233
+ // to the cell values and it will refuse a request for new subscription
234
+ for (var _i = 0, _Object$keys = Object.keys(this._cellValuesRetainCountByFieldId); _i < _Object$keys.length; _i++) {
235
+ var fieldId = _Object$keys[_i];
236
+
237
+ while (this._cellValuesRetainCountByFieldId[fieldId] && this._cellValuesRetainCountByFieldId[fieldId] > 0) {
238
+ this.unloadCellValuesInFieldIds([fieldId]);
239
+ }
240
+ }
241
+
242
+ this._forceUnload(); // similarly unsubscribe from the view data.
243
+ // this comes after _forceUnload to avoid over releasing the table data.
463
244
 
464
- this._queryManager.forceUnloadWatchesOnWatchableModel(this);
465
245
 
466
- var _iteratorNormalCompletion5 = true;
467
- var _didIteratorError5 = false;
468
- var _iteratorError5 = undefined;
246
+ var _iteratorNormalCompletion2 = true;
247
+ var _didIteratorError2 = false;
248
+ var _iteratorError2 = undefined;
469
249
 
470
250
  try {
471
- for (var _iterator5 = this._forceUnloadCallbacks[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
472
- var forceUnloadCallback = _step5.value;
473
- forceUnloadCallback();
474
- } // similarly unsubscribe from the view data.
475
- // this comes after _forceUnload to avoid over releasing the table data.
251
+ for (var _iterator2 = (0, _private_utils.values)(this._viewDataStoresByViewId)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
252
+ var viewDataStore = _step2.value;
476
253
 
254
+ viewDataStore.__onDataDeletion();
255
+ }
477
256
  } catch (err) {
478
- _didIteratorError5 = true;
479
- _iteratorError5 = err;
257
+ _didIteratorError2 = true;
258
+ _iteratorError2 = err;
480
259
  } finally {
481
260
  try {
482
- if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
483
- _iterator5.return();
261
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
262
+ _iterator2.return();
484
263
  }
485
264
  } finally {
486
- if (_didIteratorError5) {
487
- throw _iteratorError5;
265
+ if (_didIteratorError2) {
266
+ throw _iteratorError2;
488
267
  }
489
268
  }
490
269
  }
270
+ }
271
+ /**
272
+ * Record metadata means record IDs, createdTime, and commentCount are loaded.
273
+ * Record metadata must be loaded before creating, deleting, or updating records.
274
+ */
491
275
 
492
- var _iteratorNormalCompletion6 = true;
493
- var _didIteratorError6 = false;
494
- var _iteratorError6 = undefined;
276
+ }, {
277
+ key: "loadRecordMetadataAsync",
278
+ value: function loadRecordMetadataAsync() {
279
+ return _regenerator.default.async(function loadRecordMetadataAsync$(_context) {
280
+ while (1) {
281
+ switch (_context.prev = _context.next) {
282
+ case 0:
283
+ _context.next = 2;
284
+ return _regenerator.default.awrap(this.loadCellValuesInFieldIdsAsync([this._getFieldIdForCausingRecordMetadataToLoad()]));
495
285
 
496
- try {
497
- for (var _iterator6 = (0, _private_utils.values)(this._viewDataStoresByViewId)[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
498
- var viewDataStore = _step6.value;
286
+ case 2:
287
+ return _context.abrupt("return", _context.sent);
499
288
 
500
- viewDataStore.__onDataDeletion();
501
- }
502
- } catch (err) {
503
- _didIteratorError6 = true;
504
- _iteratorError6 = err;
505
- } finally {
506
- try {
507
- if (!_iteratorNormalCompletion6 && _iterator6.return != null) {
508
- _iterator6.return();
509
- }
510
- } finally {
511
- if (_didIteratorError6) {
512
- throw _iteratorError6;
289
+ case 3:
290
+ case "end":
291
+ return _context.stop();
513
292
  }
514
293
  }
515
- }
516
- } // TODO: (#proj-blocks-sdk-record-limits) Determine if we need this function,
517
- // we shouldn't need to load the primary field in order to load metadata.
518
- // (but may want to require it for the first version of the sdk we release)
519
-
294
+ }, null, this);
295
+ }
296
+ }, {
297
+ key: "unloadRecordMetadata",
298
+ value: function unloadRecordMetadata() {
299
+ this.unloadCellValuesInFieldIds([this._getFieldIdForCausingRecordMetadataToLoad()]);
300
+ }
520
301
  }, {
521
302
  key: "_getFieldIdForCausingRecordMetadataToLoad",
522
303
  value: function _getFieldIdForCausingRecordMetadataToLoad() {
@@ -527,276 +308,770 @@ function (_AbstractModelWithAsy) {
527
308
  return this._primaryFieldId;
528
309
  }
529
310
  }, {
530
- key: "isRecordCellValueLoadedForFieldId",
531
- value: function isRecordCellValueLoadedForFieldId(recordId, fieldId) {
532
- return this._getRecordStoreForTable().isRecordCellValueLoadedForFieldId(recordId, fieldId);
533
- }
534
- }, {
535
- key: "getRecordCellValueByFieldId",
536
- value: function getRecordCellValueByFieldId(recordId, fieldId) {
537
- return this._getRecordStoreForTable().getRecordCellValueByFieldId(recordId, fieldId);
538
- }
539
- }, {
540
- key: "getRecordCommentCount",
541
- value: function getRecordCommentCount(recordId) {
542
- return this._getRecordStoreForTable().getRecordCommentCount(recordId);
311
+ key: "areCellValuesLoadedForFieldId",
312
+ value: function areCellValuesLoadedForFieldId(fieldId) {
313
+ return this.isDataLoaded || this._areCellValuesLoadedByFieldId[fieldId] || false;
543
314
  }
544
315
  }, {
545
- key: "getRecordCreatedTime",
546
- value: function getRecordCreatedTime(recordId) {
547
- return this._getRecordStoreForTable().getRecordCreatedTime(recordId);
548
- }
549
- }, {
550
- key: "_loadCellValuesForQueryAsync",
551
- value: function _loadCellValuesForQueryAsync(query) {
552
- var querySpecIdPromise, onForceUnload, querySpecId;
553
- return _regenerator.default.async(function _loadCellValuesForQueryAsync$(_context2) {
316
+ key: "loadCellValuesInFieldIdsAsync",
317
+ value: function loadCellValuesInFieldIdsAsync(fieldIds) {
318
+ var fieldIdsWhichAreNotAlreadyLoadedOrLoading, pendingLoadPromises, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, _fieldId, pendingLoadPromise, loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, fieldId;
319
+
320
+ return _regenerator.default.async(function loadCellValuesInFieldIdsAsync$(_context2) {
554
321
  while (1) {
555
322
  switch (_context2.prev = _context2.next) {
556
323
  case 0:
557
- this._assertNotForceUnloaded(); // We send all queries through query manager. PublicAirtableInterface does
558
- // all reference counting for queries, and doesn't send cell data over the bridge
559
- // if it's already being tracked in the PublicRecordStore.
560
-
561
-
562
- querySpecIdPromise = this._sdk._queryManager.fetchAndSubscribeToQueryAsync(_objectSpread({}, query, {
563
- sourceTableId: this.tableId
564
- }));
565
-
566
- onForceUnload = () => {
567
- var querySpecId;
568
- return _regenerator.default.async(function _callee$(_context) {
569
- while (1) {
570
- switch (_context.prev = _context.next) {
571
- case 0:
572
- _context.next = 2;
573
- return _regenerator.default.awrap(querySpecIdPromise);
574
-
575
- case 2:
576
- querySpecId = _context.sent;
577
-
578
- if (this._loadedQuerySpecIds.has(querySpecId)) {
579
- this._unloadCellValuesForQueryIdsIgnoringForceUnloadedness([querySpecId]);
580
- }
581
-
582
- case 4:
583
- case "end":
584
- return _context.stop();
324
+ this._assertNotForceUnloaded();
325
+
326
+ fieldIdsWhichAreNotAlreadyLoadedOrLoading = [];
327
+ pendingLoadPromises = [];
328
+ _iteratorNormalCompletion3 = true;
329
+ _didIteratorError3 = false;
330
+ _iteratorError3 = undefined;
331
+ _context2.prev = 6;
332
+
333
+ for (_iterator3 = fieldIds[Symbol.iterator](); !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
334
+ _fieldId = _step3.value;
335
+
336
+ if (this._cellValuesRetainCountByFieldId[_fieldId] !== undefined) {
337
+ this._cellValuesRetainCountByFieldId[_fieldId]++;
338
+ } else {
339
+ this._cellValuesRetainCountByFieldId[_fieldId] = 1;
340
+ } // NOTE: we don't use this.areCellValuesLoadedForFieldId() here because
341
+ // that will return true if the cell values are loaded as a result
342
+ // of the entire table being loaded. In that scenario, we still
343
+ // want to separately load the cell values for the field so there
344
+ // is a separate subscription. Otherwise, when the table data unloads,
345
+ // the field data would unload as well. This can be improved by just
346
+ // subscribing to the field data without fetching it, since the cell
347
+ // values are already in the block frame.
348
+
349
+
350
+ if (!this._areCellValuesLoadedByFieldId[_fieldId]) {
351
+ pendingLoadPromise = this._pendingCellValuesLoadPromiseByFieldId[_fieldId];
352
+
353
+ if (pendingLoadPromise) {
354
+ pendingLoadPromises.push(pendingLoadPromise);
355
+ } else {
356
+ fieldIdsWhichAreNotAlreadyLoadedOrLoading.push(_fieldId);
357
+ }
358
+ }
359
+ }
360
+
361
+ _context2.next = 14;
362
+ break;
363
+
364
+ case 10:
365
+ _context2.prev = 10;
366
+ _context2.t0 = _context2["catch"](6);
367
+ _didIteratorError3 = true;
368
+ _iteratorError3 = _context2.t0;
369
+
370
+ case 14:
371
+ _context2.prev = 14;
372
+ _context2.prev = 15;
373
+
374
+ if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
375
+ _iterator3.return();
376
+ }
377
+
378
+ case 17:
379
+ _context2.prev = 17;
380
+
381
+ if (!_didIteratorError3) {
382
+ _context2.next = 20;
383
+ break;
384
+ }
385
+
386
+ throw _iteratorError3;
387
+
388
+ case 20:
389
+ return _context2.finish(17);
390
+
391
+ case 21:
392
+ return _context2.finish(14);
393
+
394
+ case 22:
395
+ if (!(fieldIdsWhichAreNotAlreadyLoadedOrLoading.length > 0)) {
396
+ _context2.next = 45;
397
+ break;
398
+ }
399
+
400
+ // Could inline _loadCellValuesInFieldIdsAsync, but following the
401
+ // pattern from AbstractModelWithAsyncData where the public method
402
+ // is responsible for updating retain counts and the private method
403
+ // actually fetches data.
404
+ loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise = this._loadCellValuesInFieldIdsAsync(fieldIdsWhichAreNotAlreadyLoadedOrLoading);
405
+ pendingLoadPromises.push(loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise);
406
+ _iteratorNormalCompletion4 = true;
407
+ _didIteratorError4 = false;
408
+ _iteratorError4 = undefined;
409
+ _context2.prev = 28;
410
+
411
+ for (_iterator4 = fieldIdsWhichAreNotAlreadyLoadedOrLoading[Symbol.iterator](); !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
412
+ fieldId = _step4.value;
413
+ this._pendingCellValuesLoadPromiseByFieldId[fieldId] = loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise;
414
+ } // Doing `.then` instead of performing these actions directly in
415
+ // _loadCellValuesInFieldIdsAsync so this is similar to
416
+ // AbstractModelWithAsyncData. The idea is to refactor to avoid code
417
+ // duplication, so keeping them similar for now hopefully will make the
418
+ // refactor simpler.
419
+
420
+
421
+ _context2.next = 36;
422
+ break;
423
+
424
+ case 32:
425
+ _context2.prev = 32;
426
+ _context2.t1 = _context2["catch"](28);
427
+ _didIteratorError4 = true;
428
+ _iteratorError4 = _context2.t1;
429
+
430
+ case 36:
431
+ _context2.prev = 36;
432
+ _context2.prev = 37;
433
+
434
+ if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
435
+ _iterator4.return();
436
+ }
437
+
438
+ case 39:
439
+ _context2.prev = 39;
440
+
441
+ if (!_didIteratorError4) {
442
+ _context2.next = 42;
443
+ break;
444
+ }
445
+
446
+ throw _iteratorError4;
447
+
448
+ case 42:
449
+ return _context2.finish(39);
450
+
451
+ case 43:
452
+ return _context2.finish(36);
453
+
454
+ case 44:
455
+ loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise.then(changedKeys => {
456
+ var _iteratorNormalCompletion5 = true;
457
+ var _didIteratorError5 = false;
458
+ var _iteratorError5 = undefined;
459
+
460
+ try {
461
+ for (var _iterator5 = fieldIdsWhichAreNotAlreadyLoadedOrLoading[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
462
+ var fieldId = _step5.value;
463
+ this._areCellValuesLoadedByFieldId[fieldId] = true;
464
+ this._pendingCellValuesLoadPromiseByFieldId[fieldId] = undefined;
465
+ }
466
+ } catch (err) {
467
+ _didIteratorError5 = true;
468
+ _iteratorError5 = err;
469
+ } finally {
470
+ try {
471
+ if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
472
+ _iterator5.return();
473
+ }
474
+ } finally {
475
+ if (_didIteratorError5) {
476
+ throw _iteratorError5;
585
477
  }
586
478
  }
587
- }, null, this);
588
- };
479
+ }
589
480
 
590
- this._forceUnloadCallbacks.push(onForceUnload);
481
+ var _iteratorNormalCompletion6 = true;
482
+ var _didIteratorError6 = false;
483
+ var _iteratorError6 = undefined;
591
484
 
592
- _context2.next = 6;
593
- return _regenerator.default.awrap(querySpecIdPromise);
485
+ try {
486
+ for (var _iterator6 = changedKeys[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
487
+ var key = _step6.value;
594
488
 
595
- case 6:
596
- querySpecId = _context2.sent;
489
+ this._onChange(key);
490
+ }
491
+ } catch (err) {
492
+ _didIteratorError6 = true;
493
+ _iteratorError6 = err;
494
+ } finally {
495
+ try {
496
+ if (!_iteratorNormalCompletion6 && _iterator6.return != null) {
497
+ _iterator6.return();
498
+ }
499
+ } finally {
500
+ if (_didIteratorError6) {
501
+ throw _iteratorError6;
502
+ }
503
+ }
504
+ }
505
+ });
597
506
 
598
- this._loadedQuerySpecIds.add(querySpecId);
507
+ case 45:
508
+ // Since we are incrementing fieldIds, it's necessary to restart any pending timeouts
509
+ // to unload data. This is because it's possible for a timeout to fire while a queryResult
510
+ // is actively unloading, and erroneously unload data. Data must be unloaded _after_ the queryResult.
511
+ this._restartTimeoutToUnloadFieldIdsIfTimeoutIsActive();
599
512
 
600
- return _context2.abrupt("return", querySpecId);
513
+ _context2.next = 48;
514
+ return _regenerator.default.awrap(Promise.all(pendingLoadPromises));
601
515
 
602
- case 9:
516
+ case 48:
603
517
  case "end":
604
518
  return _context2.stop();
605
519
  }
606
520
  }
607
- }, null, this);
521
+ }, null, this, [[6, 10, 14, 22], [15,, 17, 21], [28, 32, 36, 44], [37,, 39, 43]]);
608
522
  }
609
523
  }, {
610
- key: "_unloadCellValuesForQueryIds",
611
- value: function _unloadCellValuesForQueryIds(queryIds) {
612
- if (this._isForceUnloaded) {
613
- return;
614
- }
524
+ key: "_loadCellValuesInFieldIdsAsync",
525
+ value: function _loadCellValuesInFieldIdsAsync(fieldIds) {
526
+ var _ref, newRecordsById, existingRecordsById, _iteratorNormalCompletion7, _didIteratorError7, _iteratorError7, _iterator7, _step7, _step7$value, recordId, newRecordObj, existingRecordObj, isCommentCountTypesSame, isCreatedTimeTypesSame, existingCellValuesByFieldId, i, fieldId, changedKeys;
527
+
528
+ return _regenerator.default.async(function _loadCellValuesInFieldIdsAsync$(_context3) {
529
+ while (1) {
530
+ switch (_context3.prev = _context3.next) {
531
+ case 0:
532
+ _context3.next = 2;
533
+ return _regenerator.default.awrap(this._airtableInterface.fetchAndSubscribeToCellValuesInFieldsAsync(this.tableId, fieldIds));
534
+
535
+ case 2:
536
+ _ref = _context3.sent;
537
+ newRecordsById = _ref.recordsById;
538
+
539
+ // Merge with existing data.
540
+ if (!this._data.recordsById) {
541
+ this._data.recordsById = {};
542
+ }
543
+
544
+ existingRecordsById = this._data.recordsById;
545
+ _iteratorNormalCompletion7 = true;
546
+ _didIteratorError7 = false;
547
+ _iteratorError7 = undefined;
548
+ _context3.prev = 9;
549
+
550
+ for (_iterator7 = (0, _private_utils.entries)((0, _private_utils.cast)(newRecordsById))[Symbol.iterator](); !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
551
+ _step7$value = (0, _slicedToArray2.default)(_step7.value, 2), recordId = _step7$value[0], newRecordObj = _step7$value[1];
552
+
553
+ if (!(0, _private_utils.has)(existingRecordsById, recordId)) {
554
+ existingRecordsById[recordId] = newRecordObj;
555
+ } else {
556
+ existingRecordObj = existingRecordsById[recordId]; // Metadata (createdTime, commentCount) should generally be up to date,
557
+ // but can be out of date in the rare scenario where realtime
558
+ // data has not yet been delivered to the SDK, but is populated in hyperbase
559
+ // at the time this new fetch is executed.
560
+ // istanbul ignore next
561
+
562
+ if (existingRecordObj.commentCount !== newRecordObj.commentCount) {
563
+ isCommentCountTypesSame = typeof existingRecordObj.commentCount !== typeof newRecordObj.commentCount;
564
+ (0, _error_utils.logErrorToSentry)('comment count out of sync - types are same: %s', {
565
+ isCommentCountTypesSame
566
+ });
567
+ } // istanbul ignore next
568
+
569
+
570
+ if (existingRecordObj.createdTime !== newRecordObj.createdTime) {
571
+ isCreatedTimeTypesSame = typeof existingRecordObj.createdTime !== typeof newRecordObj.createdTime;
572
+ (0, _error_utils.logErrorToSentry)('created time out of sync - types are same: %s', {
573
+ isCreatedTimeTypesSame
574
+ });
575
+ }
576
+
577
+ if (!existingRecordObj.cellValuesByFieldId) {
578
+ existingRecordObj.cellValuesByFieldId = {};
579
+ }
580
+
581
+ existingCellValuesByFieldId = existingRecordObj.cellValuesByFieldId;
615
582
 
616
- this._unloadCellValuesForQueryIdsIgnoringForceUnloadedness(queryIds);
583
+ for (i = 0; i < fieldIds.length; i++) {
584
+ fieldId = fieldIds[i];
585
+ existingCellValuesByFieldId[fieldId] = newRecordObj.cellValuesByFieldId ? newRecordObj.cellValuesByFieldId[fieldId] : undefined;
586
+ }
587
+ }
588
+ }
589
+
590
+ _context3.next = 17;
591
+ break;
592
+
593
+ case 13:
594
+ _context3.prev = 13;
595
+ _context3.t0 = _context3["catch"](9);
596
+ _didIteratorError7 = true;
597
+ _iteratorError7 = _context3.t0;
598
+
599
+ case 17:
600
+ _context3.prev = 17;
601
+ _context3.prev = 18;
602
+
603
+ if (!_iteratorNormalCompletion7 && _iterator7.return != null) {
604
+ _iterator7.return();
605
+ }
606
+
607
+ case 20:
608
+ _context3.prev = 20;
609
+
610
+ if (!_didIteratorError7) {
611
+ _context3.next = 23;
612
+ break;
613
+ }
614
+
615
+ throw _iteratorError7;
616
+
617
+ case 23:
618
+ return _context3.finish(20);
619
+
620
+ case 24:
621
+ return _context3.finish(17);
622
+
623
+ case 25:
624
+ changedKeys = fieldIds.map(fieldId => WatchableCellValuesInFieldKeyPrefix + fieldId); // Need to trigger onChange for records and recordIds since watching either
625
+ // of those causes record metadata to be loaded (via _getFieldIdForCausingRecordMetadataToLoad)
626
+ // and by convention we trigger a change event when data loads.
627
+
628
+ changedKeys.push(WatchableRecordStoreKeys.records);
629
+ changedKeys.push(WatchableRecordStoreKeys.recordIds); // Also trigger cellValues changes since the cell values in the fields
630
+ // are now loaded.
631
+
632
+ changedKeys.push(WatchableRecordStoreKeys.cellValues);
633
+ return _context3.abrupt("return", changedKeys);
634
+
635
+ case 30:
636
+ case "end":
637
+ return _context3.stop();
638
+ }
639
+ }
640
+ }, null, this, [[9, 13, 17, 25], [18,, 20, 24]]);
617
641
  }
618
642
  }, {
619
- key: "_unloadCellValuesForQueryIdsIgnoringForceUnloadedness",
620
- value: function _unloadCellValuesForQueryIdsIgnoringForceUnloadedness(queryIds) {
621
- this._queryManager.unsubscribeFromQueryIds(queryIds);
643
+ key: "unloadCellValuesInFieldIds",
644
+ value: function unloadCellValuesInFieldIds(fieldIds) {
645
+ if (this._isForceUnloaded) {
646
+ return;
647
+ }
622
648
 
623
- var _iteratorNormalCompletion7 = true;
624
- var _didIteratorError7 = false;
625
- var _iteratorError7 = undefined;
649
+ var _iteratorNormalCompletion8 = true;
650
+ var _didIteratorError8 = false;
651
+ var _iteratorError8 = undefined;
626
652
 
627
653
  try {
628
- for (var _iterator7 = queryIds[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
629
- var queryId = _step7.value;
654
+ for (var _iterator8 = fieldIds[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
655
+ var fieldId = _step8.value;
656
+ var fieldRetainCount = this._cellValuesRetainCountByFieldId[fieldId] || 0;
657
+ fieldRetainCount--;
630
658
 
631
- var queryIdWasLoaded = this._loadedQuerySpecIds.delete(queryId);
659
+ if (fieldRetainCount < 0) {
660
+ console.log('Field data over-released'); // eslint-disable-line no-console
632
661
 
633
- if (!queryIdWasLoaded) {
634
- (0, _warning.default)('Over unsubscribed from record store');
662
+ fieldRetainCount = 0;
635
663
  }
636
- }
664
+
665
+ this._cellValuesRetainCountByFieldId[fieldId] = fieldRetainCount;
666
+ } // Don't unload immediately. Wait a while in case something else
667
+ // requests the data, so we can avoid going back to liveapp or
668
+ // the network.
669
+
637
670
  } catch (err) {
638
- _didIteratorError7 = true;
639
- _iteratorError7 = err;
671
+ _didIteratorError8 = true;
672
+ _iteratorError8 = err;
640
673
  } finally {
641
674
  try {
642
- if (!_iteratorNormalCompletion7 && _iterator7.return != null) {
643
- _iterator7.return();
675
+ if (!_iteratorNormalCompletion8 && _iterator8.return != null) {
676
+ _iterator8.return();
644
677
  }
645
678
  } finally {
646
- if (_didIteratorError7) {
647
- throw _iteratorError7;
679
+ if (_didIteratorError8) {
680
+ throw _iteratorError8;
648
681
  }
649
682
  }
650
683
  }
651
- }
684
+
685
+ this._startTimeoutToUnloadForFieldIdsIfNeeded();
686
+ } // This unloads all fields where the retain count is at zero, and if any other
687
+ // request to unload fields is pending - cancels it and restarts it.
688
+ // This is important because fields must always be unloaded at least __DATA_UNLOAD_DELAY_MS
689
+ // after the unload is requested so that any QueryResults relying on them properly
690
+ // unload either first, or at the same time
691
+
652
692
  }, {
653
- key: "loadViewDataStoreAndCellValuesForFieldIdsAsync",
654
- value: function loadViewDataStoreAndCellValuesForFieldIdsAsync(viewId, fieldIdsToLoadOrNullIfAllFields) {
655
- var fieldSelection;
656
- return _regenerator.default.async(function loadViewDataStoreAndCellValuesForFieldIdsAsync$(_context3) {
657
- while (1) {
658
- switch (_context3.prev = _context3.next) {
659
- case 0:
660
- // We do not need to initialize a ViewDataStore, as it's a thin wrapper around the
661
- // hyperbase viewMetadataStore which can be initialized after a query inflates the data.
662
- // A complicating factor is what happens if (this) recordStore or viewDataStore are
663
- // forceUnload'ed. It is fine to track the query in only record store because:
664
- // - If either is force unloaded without the other the query must be kept active
665
- // - If a recordStore is force unloaded the children viewDataStores are also unloaded
666
- // because the table has been deleted. Therefore we can track it in only record_store.
667
- fieldSelection = fieldIdsToLoadOrNullIfAllFields ? {
668
- type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
669
- fieldIds: fieldIdsToLoadOrNullIfAllFields
670
- } : {
671
- type: _block_query_spec.BlockFieldSelectionSpecType.ALL_FIELDS_FROM_TABLE
672
- }; // By issuing this query we both populate cell values requested AND view metadata
673
- // Cell values requested will be accessible inside record_store
674
-
675
- _context3.next = 3;
676
- return _regenerator.default.awrap(this._loadCellValuesForQueryAsync({
677
- sourceType: _block_query_spec.BlockQuerySourceType.VIEW,
678
- sourceViewId: viewId,
679
- recordSelection: {
680
- fieldSelection
681
- },
682
- viewMetadataSelection: {
683
- shouldIncludeVisibleRecordIds: true,
684
- shouldIncludeFieldOrder: true,
685
- shouldIncludeGroups: true,
686
- shouldIncludeGroupingLevels: true,
687
- shouldIncludeColorsForRecordId: true
688
- }
689
- }));
693
+ key: "_startTimeoutToUnloadForFieldIdsIfNeeded",
694
+ value: function _startTimeoutToUnloadForFieldIdsIfNeeded() {
695
+ var fieldIdsWithZeroRetainCount = [];
690
696
 
691
- case 3:
692
- return _context3.abrupt("return", _context3.sent);
697
+ for (var _i2 = 0, _Object$entries = Object.entries(this._cellValuesRetainCountByFieldId); _i2 < _Object$entries.length; _i2++) {
698
+ var _Object$entries$_i = (0, _slicedToArray2.default)(_Object$entries[_i2], 2),
699
+ fieldId = _Object$entries$_i[0],
700
+ retainCount = _Object$entries$_i[1];
693
701
 
694
- case 4:
695
- case "end":
696
- return _context3.stop();
697
- }
702
+ if (retainCount === 0) {
703
+ fieldIdsWithZeroRetainCount.push(fieldId);
698
704
  }
699
- }, null, this);
705
+ } // Cancel any pending timeouts before proceeding
706
+ // This should be canceled even if there aren't any fields to unload as that means
707
+ // that there has been loading that's occured that makes the pending request invalid
708
+
709
+
710
+ if (this._timeoutForRemovingFieldIds) {
711
+ clearTimeout(this._timeoutForRemovingFieldIds);
712
+ this._timeoutForRemovingFieldIds = null;
713
+ }
714
+
715
+ if (fieldIdsWithZeroRetainCount.length > 0) {
716
+ this._timeoutForRemovingFieldIds = setTimeout(() => {
717
+ // Make sure the retain count is still zero, since it may
718
+ // have been incremented before the timeout fired.
719
+ var fieldIdsToUnload = fieldIdsWithZeroRetainCount.filter(fieldId => {
720
+ // It's necessary to also check that the field is loaded, as it's possible
721
+ // for an unload to trigger with fields that have already been removed.
722
+ return this._cellValuesRetainCountByFieldId[fieldId] === 0 && this._areCellValuesLoadedByFieldId[fieldId];
723
+ }); // istanbul ignore else
724
+
725
+ if (fieldIdsToUnload.length > 0) {
726
+ // Set _areCellValuesLoadedByFieldId to false before calling _unloadCellValuesInFieldIds
727
+ // since _unloadCellValuesInFieldIds will check if *any* fields are still loaded.
728
+ var _iteratorNormalCompletion9 = true;
729
+ var _didIteratorError9 = false;
730
+ var _iteratorError9 = undefined;
731
+
732
+ try {
733
+ for (var _iterator9 = fieldIdsToUnload[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
734
+ var fieldId = _step9.value;
735
+ this._areCellValuesLoadedByFieldId[fieldId] = false;
736
+ }
737
+ } catch (err) {
738
+ _didIteratorError9 = true;
739
+ _iteratorError9 = err;
740
+ } finally {
741
+ try {
742
+ if (!_iteratorNormalCompletion9 && _iterator9.return != null) {
743
+ _iterator9.return();
744
+ }
745
+ } finally {
746
+ if (_didIteratorError9) {
747
+ throw _iteratorError9;
748
+ }
749
+ }
750
+ }
751
+
752
+ this._unloadCellValuesInFieldIds(fieldIdsToUnload);
753
+ } else {
754
+ // This shouldn't be possible because we always cancel the timer if fieldIds loadedness
755
+ // status ever changes
756
+ (0, _error_utils.logErrorToSentry)('fieldIdsToUnload is empty, this likely means the unload timer is not properly reset.');
757
+ }
758
+
759
+ this._timeoutForRemovingFieldIds = null;
760
+ }, _abstract_model_with_async_data.default.__DATA_UNLOAD_DELAY_MS);
761
+ }
700
762
  }
701
763
  }, {
702
- key: "unloadViewDataStoreAndCellValuesWithQueryIds",
703
- value: function unloadViewDataStoreAndCellValuesWithQueryIds(queryIds) {
704
- // Note that we do not update _numQueriesSubscribedToAllRecords since this query
705
- // does not subscribe to all cells in a table.
706
- this._unloadCellValuesForQueryIds(queryIds);
764
+ key: "_restartTimeoutToUnloadFieldIdsIfTimeoutIsActive",
765
+ value: function _restartTimeoutToUnloadFieldIdsIfTimeoutIsActive() {
766
+ if (this._timeoutForRemovingFieldIds) {
767
+ this._startTimeoutToUnloadForFieldIdsIfNeeded();
768
+ }
707
769
  }
708
770
  }, {
709
- key: "loadAllCellValuesInFieldIdsAsync",
710
- value: function loadAllCellValuesInFieldIdsAsync(fieldIds) {
711
- var queryId;
712
- return _regenerator.default.async(function loadAllCellValuesInFieldIdsAsync$(_context4) {
771
+ key: "_unloadCellValuesInFieldIds",
772
+ value: function _unloadCellValuesInFieldIds(fieldIds) {
773
+ this._airtableInterface.unsubscribeFromCellValuesInFields(this.tableId, fieldIds);
774
+
775
+ this._afterUnloadDataOrUnloadCellValuesInFieldIds(fieldIds);
776
+ }
777
+ }, {
778
+ key: "_loadDataAsync",
779
+ value: function _loadDataAsync() {
780
+ var tableData, changedKeys, _iteratorNormalCompletion10, _didIteratorError10, _iteratorError10, _iterator10, _step10, fieldId;
781
+
782
+ return _regenerator.default.async(function _loadDataAsync$(_context4) {
713
783
  while (1) {
714
784
  switch (_context4.prev = _context4.next) {
715
785
  case 0:
716
786
  _context4.next = 2;
717
- return _regenerator.default.awrap(this._loadCellValuesForQueryAsync({
718
- sourceType: _block_query_spec.BlockQuerySourceType.TABLE,
719
- recordSelection: {
720
- fieldSelection: {
721
- type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
722
- fieldIds
723
- }
724
- }
725
- }));
787
+ return _regenerator.default.awrap(this._airtableInterface.fetchAndSubscribeToTableDataAsync(this.tableId));
726
788
 
727
789
  case 2:
728
- queryId = _context4.sent;
729
- this._numQueriesSubscribedToAllRecords += 1;
730
- return _context4.abrupt("return", queryId);
790
+ tableData = _context4.sent;
791
+ this._data.recordsById = tableData.recordsById;
792
+ changedKeys = [WatchableRecordStoreKeys.records, WatchableRecordStoreKeys.recordIds, WatchableRecordStoreKeys.cellValues];
793
+ _iteratorNormalCompletion10 = true;
794
+ _didIteratorError10 = false;
795
+ _iteratorError10 = undefined;
796
+ _context4.prev = 8;
797
+
798
+ for (_iterator10 = (0, _private_utils.keys)(this._data.fieldsById)[Symbol.iterator](); !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
799
+ fieldId = _step10.value;
800
+ changedKeys.push(WatchableCellValuesInFieldKeyPrefix + fieldId);
801
+ }
802
+
803
+ _context4.next = 16;
804
+ break;
805
+
806
+ case 12:
807
+ _context4.prev = 12;
808
+ _context4.t0 = _context4["catch"](8);
809
+ _didIteratorError10 = true;
810
+ _iteratorError10 = _context4.t0;
811
+
812
+ case 16:
813
+ _context4.prev = 16;
814
+ _context4.prev = 17;
815
+
816
+ if (!_iteratorNormalCompletion10 && _iterator10.return != null) {
817
+ _iterator10.return();
818
+ }
819
+
820
+ case 19:
821
+ _context4.prev = 19;
822
+
823
+ if (!_didIteratorError10) {
824
+ _context4.next = 22;
825
+ break;
826
+ }
827
+
828
+ throw _iteratorError10;
829
+
830
+ case 22:
831
+ return _context4.finish(19);
731
832
 
732
- case 5:
833
+ case 23:
834
+ return _context4.finish(16);
835
+
836
+ case 24:
837
+ return _context4.abrupt("return", changedKeys);
838
+
839
+ case 25:
733
840
  case "end":
734
841
  return _context4.stop();
735
842
  }
736
843
  }
737
- }, null, this);
844
+ }, null, this, [[8, 12, 16, 24], [17,, 19, 23]]);
738
845
  }
739
846
  }, {
740
- key: "loadAllCellValuesAsync",
741
- value: function loadAllCellValuesAsync() {
742
- var queryId;
743
- return _regenerator.default.async(function loadAllCellValuesAsync$(_context5) {
744
- while (1) {
745
- switch (_context5.prev = _context5.next) {
746
- case 0:
747
- _context5.next = 2;
748
- return _regenerator.default.awrap(this._loadCellValuesForQueryAsync({
749
- sourceType: _block_query_spec.BlockQuerySourceType.TABLE,
750
- recordSelection: {
751
- fieldSelection: {
752
- type: _block_query_spec.BlockFieldSelectionSpecType.ALL_FIELDS_FROM_TABLE
753
- }
754
- }
755
- }));
847
+ key: "_unloadData",
848
+ value: function _unloadData() {
849
+ this._airtableInterface.unsubscribeFromTableData(this.tableId);
756
850
 
757
- case 2:
758
- queryId = _context5.sent;
759
- this._numQueriesSubscribedToAllRecords += 1;
760
- return _context5.abrupt("return", queryId);
851
+ this._afterUnloadDataOrUnloadCellValuesInFieldIds();
852
+ }
853
+ }, {
854
+ key: "_afterUnloadDataOrUnloadCellValuesInFieldIds",
855
+ value: function _afterUnloadDataOrUnloadCellValuesInFieldIds(unloadedFieldIds) {
856
+ var areAnyFieldsLoaded = this.isDataLoaded || (0, _private_utils.values)(this._areCellValuesLoadedByFieldId).some(isLoaded => isLoaded);
857
+
858
+ if (!this.isDeleted) {
859
+ if (!areAnyFieldsLoaded) {
860
+ this._data.recordsById = undefined;
861
+ } else if (!this.isDataLoaded) {
862
+ var fieldIdsToClear; // This should be impossible - for fields should always be loaded
863
+ // when attempting to unload specific fields. This codepath was previously possible
864
+ // due to a bug. It could be converted to an invariant, but that is higher risk.
865
+ // istanbul ignore if
866
+
867
+ if (unloadedFieldIds) {
868
+ // Specific fields were unloaded, so clear out the cell values for those fields.
869
+ fieldIdsToClear = unloadedFieldIds;
870
+ (0, _error_utils.logErrorToSentry)('Field Ids are being unloaded when record_store is unloaded');
871
+ } else {
872
+ // The entire table was unloaded, but some individual fields are still loaded.
873
+ // We need to clear out the cell values of every field that was unloaded.
874
+ // This is kind of slow, but hopefully uncommon.
875
+ var fieldIds = Object.keys(this._data.fieldsById);
876
+ fieldIdsToClear = fieldIds.filter(fieldId => !this._areCellValuesLoadedByFieldId[fieldId]);
877
+ }
761
878
 
762
- case 5:
763
- case "end":
764
- return _context5.stop();
879
+ var recordsById = this._data.recordsById;
880
+ var _iteratorNormalCompletion11 = true;
881
+ var _didIteratorError11 = false;
882
+ var _iteratorError11 = undefined;
883
+
884
+ try {
885
+ for (var _iterator11 = (0, _private_utils.values)(recordsById || {})[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) {
886
+ var recordObj = _step11.value;
887
+
888
+ for (var i = 0; i < fieldIdsToClear.length; i++) {
889
+ var fieldId = fieldIdsToClear[i];
890
+
891
+ if (recordObj.cellValuesByFieldId) {
892
+ recordObj.cellValuesByFieldId[fieldId] = undefined;
893
+ }
894
+ }
895
+ }
896
+ } catch (err) {
897
+ _didIteratorError11 = true;
898
+ _iteratorError11 = err;
899
+ } finally {
900
+ try {
901
+ if (!_iteratorNormalCompletion11 && _iterator11.return != null) {
902
+ _iterator11.return();
903
+ }
904
+ } finally {
905
+ if (_didIteratorError11) {
906
+ throw _iteratorError11;
907
+ }
908
+ }
765
909
  }
766
910
  }
767
- }, null, this);
911
+ }
912
+
913
+ if (!areAnyFieldsLoaded) {
914
+ this._recordModelsById = {};
915
+ }
768
916
  }
769
917
  }, {
770
- key: "unloadCellValuesForQueryIdsThatAreSubscribedToAllCells",
771
- value: function unloadCellValuesForQueryIdsThatAreSubscribedToAllCells(queryIds) {
772
- this._unloadCellValuesForQueryIds(queryIds);
918
+ key: "triggerOnChangeForDirtyPaths",
919
+ value: function triggerOnChangeForDirtyPaths(dirtyPaths) {
920
+ if (this.isRecordMetadataLoaded && dirtyPaths.recordsById) {
921
+ // Since tables don't have a record order, need to detect if a record
922
+ // was created or deleted and trigger onChange for records.
923
+ var dirtyFieldIdsSet = {};
924
+ var addedRecordIds = [];
925
+ var removedRecordIds = [];
926
+ var _iteratorNormalCompletion12 = true;
927
+ var _didIteratorError12 = false;
928
+ var _iteratorError12 = undefined;
773
929
 
774
- this._numQueriesSubscribedToAllRecords -= queryIds.length;
775
- } // Maybe add a comment "pending removal of inheriting from parent class AbstractModelWithAsyncData"
776
- // istanbul ignore next
930
+ try {
931
+ for (var _iterator12 = (0, _private_utils.entries)(dirtyPaths.recordsById)[Symbol.iterator](), _step12; !(_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done); _iteratorNormalCompletion12 = true) {
932
+ var _step12$value = (0, _slicedToArray2.default)(_step12.value, 2),
933
+ recordId = _step12$value[0],
934
+ dirtyRecordPaths = _step12$value[1];
935
+
936
+ if (dirtyRecordPaths && dirtyRecordPaths._isDirty) {
937
+ // If the entire record is dirty, it was either created or deleted.
938
+ (0, _error_utils.invariant)(this._data.recordsById, 'No recordsById');
939
+
940
+ if ((0, _private_utils.has)(this._data.recordsById, recordId)) {
941
+ addedRecordIds.push(recordId);
942
+ } else {
943
+ removedRecordIds.push(recordId);
944
+ var recordModel = this._recordModelsById[recordId];
945
+
946
+ if (recordModel) {
947
+ // Remove the Record model if it was deleted.
948
+ delete this._recordModelsById[recordId];
949
+ }
950
+ }
951
+ } else {
952
+ var _recordModel = this._recordModelsById[recordId];
777
953
 
778
- }, {
779
- key: "_loadDataAsync",
780
- value: function _loadDataAsync() {
781
- return _regenerator.default.async(function _loadDataAsync$(_context6) {
782
- while (1) {
783
- switch (_context6.prev = _context6.next) {
784
- case 0:
785
- throw (0, _error_utils.spawnError)('Internal error - SDK called loadData on record_store');
954
+ if (_recordModel) {
955
+ _recordModel.__triggerOnChangeForDirtyPaths(dirtyRecordPaths);
956
+ }
957
+ }
786
958
 
787
- case 1:
788
- case "end":
789
- return _context6.stop();
959
+ var cellValuesByFieldId = dirtyRecordPaths.cellValuesByFieldId;
960
+
961
+ if (cellValuesByFieldId) {
962
+ for (var _i3 = 0, _Object$keys2 = Object.keys(cellValuesByFieldId); _i3 < _Object$keys2.length; _i3++) {
963
+ var fieldId = _Object$keys2[_i3];
964
+ dirtyFieldIdsSet[fieldId] = true;
965
+ }
966
+ }
967
+ } // Now that we've composed our created/deleted record ids arrays, let's fire
968
+ // the records onChange event if any records were created or deleted.
969
+
970
+ } catch (err) {
971
+ _didIteratorError12 = true;
972
+ _iteratorError12 = err;
973
+ } finally {
974
+ try {
975
+ if (!_iteratorNormalCompletion12 && _iterator12.return != null) {
976
+ _iterator12.return();
977
+ }
978
+ } finally {
979
+ if (_didIteratorError12) {
980
+ throw _iteratorError12;
981
+ }
790
982
  }
791
983
  }
792
- });
793
- } // Maybe add a comment "pending removal of inheriting from parent class AbstractModelWithAsyncData"
794
- // istanbul ignore next
795
984
 
796
- }, {
797
- key: "_unloadData",
798
- value: function _unloadData() {
799
- throw (0, _error_utils.spawnError)('Internal error - SDK called unloadData on record_store');
985
+ if (addedRecordIds.length > 0 || removedRecordIds.length > 0) {
986
+ this._onChange(WatchableRecordStoreKeys.records, {
987
+ addedRecordIds,
988
+ removedRecordIds
989
+ });
990
+
991
+ this._onChange(WatchableRecordStoreKeys.recordIds, {
992
+ addedRecordIds,
993
+ removedRecordIds
994
+ });
995
+ } // NOTE: this is an experimental (and somewhat messy) way to watch
996
+ // for changes to cells in a table, as an alternative to implementing
997
+ // full event bubbling. For now, it unblocks the things we want to
998
+ // build, but we may replace it.
999
+ // If we keep it, could be more efficient by not calling _onChange
1000
+ // if there are no subscribers.
1001
+ // TODO: don't trigger changes for fields that aren't supposed to be loaded
1002
+ // (in some cases, e.g. record created, liveapp will send cell values
1003
+ // that we're not subscribed to).
1004
+
1005
+
1006
+ var fieldIds = Object.freeze(Object.keys(dirtyFieldIdsSet));
1007
+ var recordIds = Object.freeze(Object.keys(dirtyPaths.recordsById));
1008
+
1009
+ if (fieldIds.length > 0 && recordIds.length > 0) {
1010
+ this._onChange(WatchableRecordStoreKeys.cellValues, {
1011
+ recordIds,
1012
+ fieldIds
1013
+ });
1014
+ }
1015
+
1016
+ var _iteratorNormalCompletion13 = true;
1017
+ var _didIteratorError13 = false;
1018
+ var _iteratorError13 = undefined;
1019
+
1020
+ try {
1021
+ for (var _iterator13 = fieldIds[Symbol.iterator](), _step13; !(_iteratorNormalCompletion13 = (_step13 = _iterator13.next()).done); _iteratorNormalCompletion13 = true) {
1022
+ var _fieldId2 = _step13.value;
1023
+
1024
+ this._onChange(WatchableCellValuesInFieldKeyPrefix + _fieldId2, recordIds, _fieldId2);
1025
+ }
1026
+ } catch (err) {
1027
+ _didIteratorError13 = true;
1028
+ _iteratorError13 = err;
1029
+ } finally {
1030
+ try {
1031
+ if (!_iteratorNormalCompletion13 && _iterator13.return != null) {
1032
+ _iterator13.return();
1033
+ }
1034
+ } finally {
1035
+ if (_didIteratorError13) {
1036
+ throw _iteratorError13;
1037
+ }
1038
+ }
1039
+ }
1040
+ }
1041
+
1042
+ if (dirtyPaths.viewOrder) {
1043
+ // clean up deleted views
1044
+ var _iteratorNormalCompletion14 = true;
1045
+ var _didIteratorError14 = false;
1046
+ var _iteratorError14 = undefined;
1047
+
1048
+ try {
1049
+ for (var _iterator14 = (0, _private_utils.entries)(this._viewDataStoresByViewId)[Symbol.iterator](), _step14; !(_iteratorNormalCompletion14 = (_step14 = _iterator14.next()).done); _iteratorNormalCompletion14 = true) {
1050
+ var _step14$value = (0, _slicedToArray2.default)(_step14.value, 2),
1051
+ viewId = _step14$value[0],
1052
+ viewDataStore = _step14$value[1];
1053
+
1054
+ if (viewDataStore.isDeleted) {
1055
+ viewDataStore.__onDataDeletion();
1056
+
1057
+ delete this._viewDataStoresByViewId[viewId];
1058
+ }
1059
+ }
1060
+ } catch (err) {
1061
+ _didIteratorError14 = true;
1062
+ _iteratorError14 = err;
1063
+ } finally {
1064
+ try {
1065
+ if (!_iteratorNormalCompletion14 && _iterator14.return != null) {
1066
+ _iterator14.return();
1067
+ }
1068
+ } finally {
1069
+ if (_didIteratorError14) {
1070
+ throw _iteratorError14;
1071
+ }
1072
+ }
1073
+ }
1074
+ }
800
1075
  }
801
1076
  }, {
802
1077
  key: "_dataOrNullIfDeleted",
@@ -808,7 +1083,9 @@ function (_AbstractModelWithAsy) {
808
1083
  }, {
809
1084
  key: "records",
810
1085
  get: function get() {
811
- var records = this.recordIds.map(recordId => {
1086
+ var recordsById = this._data.recordsById;
1087
+ (0, _error_utils.invariant)(recordsById, 'Record metadata is not loaded');
1088
+ var records = Object.keys(recordsById).map(recordId => {
812
1089
  var record = this.getRecordByIdIfExists(recordId);
813
1090
  (0, _error_utils.invariant)(record, 'record');
814
1091
  return record;
@@ -823,17 +1100,14 @@ function (_AbstractModelWithAsy) {
823
1100
  }, {
824
1101
  key: "recordIds",
825
1102
  get: function get() {
826
- (0, _error_utils.invariant)(this.isSubscribedToAllRecordsInTable, 'Record metadata is not loaded for all records in table');
827
-
828
- var recordIds = this._getRecordStoreForTable().getPartiallyLoadedRecordIds();
829
-
830
- (0, _error_utils.invariant)(recordIds, 'Record metadata is not loaded');
831
- return recordIds;
1103
+ var recordsById = this._data.recordsById;
1104
+ (0, _error_utils.invariant)(recordsById, 'Record metadata is not loaded');
1105
+ return Object.keys(recordsById);
832
1106
  }
833
1107
  }, {
834
- key: "isSubscribedToAllRecordsInTable",
1108
+ key: "isRecordMetadataLoaded",
835
1109
  get: function get() {
836
- return this._numQueriesSubscribedToAllRecords > 0;
1110
+ return !!this._data.recordsById;
837
1111
  }
838
1112
  }]);
839
1113
  return RecordStore;