@airtable/blocks 1.10.1 → 1.10.2-experimental-640bd10-20220211

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 (74) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/dist/cjs/error_utils.js +44 -2
  3. package/dist/cjs/models/grouped_record_query_result.js +5 -14
  4. package/dist/cjs/models/linked_records_query_result.js +78 -27
  5. package/dist/cjs/models/mutations.js +18 -162
  6. package/dist/cjs/models/query_manager.js +328 -0
  7. package/dist/cjs/models/record.js +310 -55
  8. package/dist/cjs/models/record_query_result.js +4 -1
  9. package/dist/cjs/models/record_store.js +557 -765
  10. package/dist/cjs/models/table.js +6 -6
  11. package/dist/cjs/models/table_or_view_query_result.js +526 -419
  12. package/dist/cjs/models/view_data_store.js +255 -295
  13. package/dist/cjs/private_utils.js +40 -0
  14. package/dist/cjs/sdk.js +12 -2
  15. package/dist/cjs/testing/abstract_mock_airtable_interface.js +57 -11
  16. package/dist/cjs/testing/fixture_data.js +268 -0
  17. package/dist/cjs/testing/mock_base_data_stores.js +876 -0
  18. package/dist/cjs/types/airtable_interface.js +17 -2
  19. package/dist/cjs/types/block_query_spec.js +85 -0
  20. package/dist/cjs/ui/use_records.js +5 -1
  21. package/dist/cjs/unstable_testing_utils.js +55 -1
  22. package/dist/cjs/watchable.js +15 -1
  23. package/dist/types/src/error_utils.d.ts +6 -0
  24. package/dist/types/src/error_utils.d.ts.map +1 -1
  25. package/dist/types/src/models/grouped_record_query_result.d.ts +3 -3
  26. package/dist/types/src/models/grouped_record_query_result.d.ts.map +1 -1
  27. package/dist/types/src/models/linked_records_query_result.d.ts.map +1 -1
  28. package/dist/types/src/models/mutations.d.ts.map +1 -1
  29. package/dist/types/src/models/query_manager.d.ts +2 -0
  30. package/dist/types/src/models/query_manager.d.ts.map +1 -0
  31. package/dist/types/src/models/record.d.ts +12 -3
  32. package/dist/types/src/models/record.d.ts.map +1 -1
  33. package/dist/types/src/models/record_query_result.d.ts +3 -2
  34. package/dist/types/src/models/record_query_result.d.ts.map +1 -1
  35. package/dist/types/src/models/record_store.d.ts.map +1 -1
  36. package/dist/types/src/models/table_or_view_query_result.d.ts +3 -5
  37. package/dist/types/src/models/table_or_view_query_result.d.ts.map +1 -1
  38. package/dist/types/src/models/view_data_store.d.ts +0 -1
  39. package/dist/types/src/models/view_data_store.d.ts.map +1 -1
  40. package/dist/types/src/models/view_metadata_query_result.d.ts +1 -1
  41. package/dist/types/src/models/view_metadata_query_result.d.ts.map +1 -1
  42. package/dist/types/src/private_utils.d.ts +24 -1
  43. package/dist/types/src/private_utils.d.ts.map +1 -1
  44. package/dist/types/src/sdk.d.ts.map +1 -1
  45. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts +11 -11
  46. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts.map +1 -1
  47. package/dist/types/{test/airtable_interface_mocks → src/testing}/fixture_data.d.ts +42 -19
  48. package/dist/types/src/testing/fixture_data.d.ts.map +1 -0
  49. package/dist/types/src/testing/mock_base_data_stores.d.ts +55 -0
  50. package/dist/types/src/testing/mock_base_data_stores.d.ts.map +1 -0
  51. package/dist/types/src/types/airtable_interface.d.ts +59 -20
  52. package/dist/types/src/types/airtable_interface.d.ts.map +1 -1
  53. package/dist/types/src/types/block_query_spec.d.ts +139 -0
  54. package/dist/types/src/types/block_query_spec.d.ts.map +1 -0
  55. package/dist/types/src/types/table.d.ts +0 -2
  56. package/dist/types/src/types/table.d.ts.map +1 -1
  57. package/dist/types/src/types/view.d.ts +3 -8
  58. package/dist/types/src/types/view.d.ts.map +1 -1
  59. package/dist/types/src/ui/record_card.d.ts +1 -1
  60. package/dist/types/src/unstable_testing_utils.d.ts +4 -1
  61. package/dist/types/src/unstable_testing_utils.d.ts.map +1 -1
  62. package/dist/types/src/watchable.d.ts.map +1 -1
  63. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts +1 -1
  64. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts.map +1 -1
  65. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts +19 -12
  66. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts.map +1 -1
  67. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts +1 -1
  68. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts.map +1 -1
  69. package/dist/types/test/test_helpers.d.ts +2 -0
  70. package/dist/types/test/test_helpers.d.ts.map +1 -1
  71. package/dist/types/test/testing/fixture_data.test.d.ts +2 -0
  72. package/dist/types/test/testing/fixture_data.test.d.ts.map +1 -0
  73. package/package.json +2 -1
  74. package/dist/types/test/airtable_interface_mocks/fixture_data.d.ts.map +0 -1
@@ -2,13 +2,7 @@
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
4
 
5
- require("core-js/modules/es.symbol");
6
-
7
- require("core-js/modules/es.symbol.description");
8
-
9
- require("core-js/modules/es.array.filter");
10
-
11
- require("core-js/modules/es.array.iterator");
5
+ require("core-js/modules/es.array.find");
12
6
 
13
7
  require("core-js/modules/es.array.map");
14
8
 
@@ -16,17 +10,17 @@ require("core-js/modules/es.array.slice");
16
10
 
17
11
  require("core-js/modules/es.object.to-string");
18
12
 
13
+ require("core-js/modules/es.object.values");
14
+
19
15
  require("core-js/modules/es.promise");
20
16
 
21
- require("core-js/modules/web.dom-collections.iterator");
17
+ require("core-js/modules/web.dom-collections.for-each");
22
18
 
23
19
  Object.defineProperty(exports, "__esModule", {
24
20
  value: true
25
21
  });
26
22
  exports.default = exports.WatchableViewDataStoreKeys = void 0;
27
23
 
28
- var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
29
-
30
24
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
31
25
 
32
26
  require("regenerator-runtime/runtime");
@@ -35,11 +29,9 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
35
29
 
36
30
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
37
31
 
38
- var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
39
-
40
32
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
41
33
 
42
- var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
34
+ var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
43
35
 
44
36
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
45
37
 
@@ -51,11 +43,14 @@ var _private_utils = require("../private_utils");
51
43
 
52
44
  var _error_utils = require("../error_utils");
53
45
 
46
+ var _airtable_interface = require("../types/airtable_interface");
47
+
48
+ var _block_query_spec = require("../types/block_query_spec");
49
+
54
50
  var _abstract_model_with_async_data = _interopRequireDefault(require("./abstract_model_with_async_data"));
55
51
 
56
52
  var WatchableViewDataStoreKeys = Object.freeze({
57
53
  visibleRecords: 'visibleRecords',
58
- visibleRecordIds: 'visibleRecordIds',
59
54
  groups: 'groups',
60
55
  groupLevels: 'groupLevels',
61
56
  recordColors: 'recordColors',
@@ -66,9 +61,11 @@ var WatchableViewDataStoreKeys = Object.freeze({
66
61
 
67
62
  exports.WatchableViewDataStoreKeys = WatchableViewDataStoreKeys;
68
63
 
69
- // ViewDataStore contains loadable data for a specific view. That means the set of visible records,
64
+ // ViewDataStore manages loadable data for a specific view. That means the set of visible records,
70
65
  // and field order/visibility information. View itself only contains core schema information. The
71
- // data here doesn't belong in View as it's record data or conditionally loaded.
66
+ // data here doesn't belong in View as it's record data or conditionally loaded and therefore
67
+ // loaded via queries (and therefore managed by publicAirtableInterface)
68
+ // TODO: (#proj-blocks-sdk-record-limits) Remove AbstractModelWithAsyncData
72
69
 
73
70
  /** @internal */
74
71
  var ViewDataStore =
@@ -80,11 +77,6 @@ function (_AbstractModelWithAsy) {
80
77
  value: function _isWatchableKey(key) {
81
78
  return (0, _private_utils.isEnumValue)(WatchableViewDataStoreKeys, key);
82
79
  }
83
- }, {
84
- key: "_shouldLoadDataForKey",
85
- value: function _shouldLoadDataForKey(key) {
86
- return true;
87
- }
88
80
  }]);
89
81
 
90
82
  function ViewDataStore(sdk, parentRecordStore, viewId) {
@@ -94,16 +86,38 @@ function (_AbstractModelWithAsy) {
94
86
  _this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(ViewDataStore).call(this, sdk, "".concat(viewId, "-ViewDataStore")));
95
87
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "viewId", void 0);
96
88
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "parentRecordStore", void 0);
97
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_mostRecentTableLoadPromise", void 0);
98
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_airtableInterface", void 0);
89
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_publicViewStore", void 0);
90
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_queryManager", void 0);
91
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_queryIdFromReferenceCountedLoadData", null);
99
92
  _this.parentRecordStore = parentRecordStore;
100
- _this._airtableInterface = sdk.__airtableInterface;
101
- _this._mostRecentTableLoadPromise = null;
93
+ _this._queryManager = sdk._queryManager;
102
94
  _this.viewId = viewId;
95
+
96
+ var publicViewStore = _this.parentRecordStore._airtableInterface.getViewMetadataStoreIfExists(_this.parentRecordStore.tableId, viewId);
97
+
98
+ (0, _error_utils.invariant)(publicViewStore, 'PublicViewStore must exist for all valid viewIds');
99
+ _this._publicViewStore = publicViewStore;
103
100
  return _this;
104
101
  }
105
102
 
106
103
  (0, _createClass2.default)(ViewDataStore, [{
104
+ key: "_assertExistsAndLoaded",
105
+
106
+ /**
107
+ * @internal
108
+ */
109
+ value: function _assertExistsAndLoaded() {
110
+ // Access data in order to throw if the data has been deleted / isn't loaded
111
+ var data = this._dataOrNullIfDeleted; // Currently it is not possible for the ViewDataStore to spawn an error on deletion
112
+ // because the parent TableOrViewQueryResult or ViewMetadataQueryResult throw their own
113
+ // errors when the view is deleted.
114
+ // istanbul ignore if
115
+
116
+ if (data === null) {
117
+ throw this._spawnErrorForDeletion();
118
+ }
119
+ }
120
+ }, {
107
121
  key: "_onChangeIsDataLoaded",
108
122
  value: function _onChangeIsDataLoaded() {// noop
109
123
  }
@@ -111,212 +125,58 @@ function (_AbstractModelWithAsy) {
111
125
  key: "__onDataDeletion",
112
126
  value: function __onDataDeletion() {
113
127
  this._forceUnload();
114
- }
115
- }, {
116
- key: "loadDataAsync",
117
- value: function loadDataAsync() {
118
- var tableLoadPromise;
119
- return _regenerator.default.async(function loadDataAsync$(_context) {
120
- while (1) {
121
- switch (_context.prev = _context.next) {
122
- case 0:
123
- // Override this method to also load table data.
124
- // NOTE: it's important that we call loadDataAsync on the table here and not in
125
- // _loadDataAsync since we want the retain counts for the view and table to increase/decrease
126
- // in lock-step. If we load table data in _loadDataAsync, the table's retain
127
- // count only increments some of the time, which leads to unexpected behavior.
128
- tableLoadPromise = this.parentRecordStore.loadRecordMetadataAsync();
129
- this._mostRecentTableLoadPromise = tableLoadPromise;
130
- _context.next = 4;
131
- return _regenerator.default.awrap((0, _get2.default)((0, _getPrototypeOf2.default)(ViewDataStore.prototype), "loadDataAsync", this).call(this));
132
-
133
- case 4:
134
- case "end":
135
- return _context.stop();
136
- }
137
- }
138
- }, null, this);
128
+
129
+ this._queryManager.forceUnloadWatchesOnWatchableModel(this);
139
130
  }
140
131
  }, {
141
132
  key: "_loadDataAsync",
142
133
  value: function _loadDataAsync() {
143
- var tableLoadPromise, _ref, _ref2, viewData, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, record;
144
-
145
- return _regenerator.default.async(function _loadDataAsync$(_context2) {
134
+ var changedKeysOnLoad;
135
+ return _regenerator.default.async(function _loadDataAsync$(_context) {
146
136
  while (1) {
147
- switch (_context2.prev = _context2.next) {
137
+ switch (_context.prev = _context.next) {
148
138
  case 0:
149
- // We need to be sure that the table data is loaded *before* we return
150
- // from this method.
151
- (0, _error_utils.invariant)(this._mostRecentTableLoadPromise, 'No table load promise');
152
- tableLoadPromise = this._mostRecentTableLoadPromise;
153
- _context2.next = 4;
154
- return _regenerator.default.awrap(Promise.all([this._airtableInterface.fetchAndSubscribeToViewDataAsync(this.parentRecordStore.tableId, this.viewId), tableLoadPromise]));
155
-
156
- case 4:
157
- _ref = _context2.sent;
158
- _ref2 = (0, _slicedToArray2.default)(_ref, 1);
159
- viewData = _ref2[0];
160
- this._data.visibleRecordIds = viewData.visibleRecordIds;
161
- this._data.fieldOrder = viewData.fieldOrder;
162
- this._data.groups = viewData.groups;
163
- this._data.groupLevels = viewData.groupLevels;
164
- this._data.colorsByRecordId = viewData.colorsByRecordId;
165
-
166
- if (!this._data.colorsByRecordId) {
167
- _context2.next = 32;
168
- break;
169
- }
170
-
171
- _iteratorNormalCompletion = true;
172
- _didIteratorError = false;
173
- _iteratorError = undefined;
174
- _context2.prev = 16;
175
-
176
- for (_iterator = this.visibleRecords[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
177
- record = _step.value;
178
-
179
- if ((0, _private_utils.has)(this._data.colorsByRecordId, record.id)) {
180
- record.__triggerOnChangeForRecordColorInViewId(this.viewId);
139
+ // TODO: (#proj-blocks-sdk-record-limits) Do we want to remove this reference counting semantic?
140
+ // We can assume that each query is identical, but it might be helpful to have all queries be
141
+ // registered across the bridge so that we can track utilization.
142
+ (0, _error_utils.invariant)(this._queryIdFromReferenceCountedLoadData === null, 'Can not reissue an identical view data store query');
143
+ _context.next = 3;
144
+ return _regenerator.default.awrap(this._queryManager.fetchAndSubscribeToQueryAsync({
145
+ sourceType: _block_query_spec.BlockQuerySourceType.VIEW,
146
+ sourceTableId: this.parentRecordStore.tableId,
147
+ sourceViewId: this.viewId,
148
+ viewMetadataSelection: {
149
+ shouldIncludeVisibleRecordIds: true,
150
+ shouldIncludeFieldOrder: true,
151
+ shouldIncludeGroups: true,
152
+ shouldIncludeGroupingLevels: true,
153
+ shouldIncludeColorsForRecordId: true
181
154
  }
182
- }
155
+ }));
183
156
 
184
- _context2.next = 24;
185
- break;
157
+ case 3:
158
+ this._queryIdFromReferenceCountedLoadData = _context.sent;
159
+ changedKeysOnLoad = Object.values(WatchableViewDataStoreKeys);
186
160
 
187
- case 20:
188
- _context2.prev = 20;
189
- _context2.t0 = _context2["catch"](16);
190
- _didIteratorError = true;
191
- _iteratorError = _context2.t0;
161
+ this._queryManager.onLoadKeys(this, changedKeysOnLoad);
192
162
 
193
- case 24:
194
- _context2.prev = 24;
195
- _context2.prev = 25;
163
+ return _context.abrupt("return", []);
196
164
 
197
- if (!_iteratorNormalCompletion && _iterator.return != null) {
198
- _iterator.return();
199
- }
200
-
201
- case 27:
202
- _context2.prev = 27;
203
-
204
- if (!_didIteratorError) {
205
- _context2.next = 30;
206
- break;
207
- }
208
-
209
- throw _iteratorError;
210
-
211
- case 30:
212
- return _context2.finish(27);
213
-
214
- case 31:
215
- return _context2.finish(24);
216
-
217
- case 32:
218
- return _context2.abrupt("return", [WatchableViewDataStoreKeys.visibleRecords, WatchableViewDataStoreKeys.visibleRecordIds, WatchableViewDataStoreKeys.allFieldIds, WatchableViewDataStoreKeys.groups, WatchableViewDataStoreKeys.groupLevels, WatchableViewDataStoreKeys.visibleFieldIds, WatchableViewDataStoreKeys.recordColors]);
219
-
220
- case 33:
165
+ case 7:
221
166
  case "end":
222
- return _context2.stop();
167
+ return _context.stop();
223
168
  }
224
169
  }
225
- }, null, this, [[16, 20, 24, 32], [25,, 27, 31]]);
226
- }
227
- }, {
228
- key: "unloadData",
229
- value: function unloadData() {
230
- // Override this method to also unload the table's data.
231
- // NOTE: it's important that we do this here, since we want the view and table's
232
- // retain counts to increment/decrement in lock-step. If we unload the table's
233
- // data in _unloadData, it leads to unexpected behavior.
234
- (0, _get2.default)((0, _getPrototypeOf2.default)(ViewDataStore.prototype), "unloadData", this).call(this);
235
- this.parentRecordStore.unloadRecordMetadata();
170
+ }, null, this);
236
171
  }
237
172
  }, {
238
173
  key: "_unloadData",
239
174
  value: function _unloadData() {
240
- this._mostRecentTableLoadPromise = null;
175
+ (0, _error_utils.invariant)(this._queryIdFromReferenceCountedLoadData, 'Can not unsubscribe from a view store that is not subscribed');
241
176
 
242
- this._airtableInterface.unsubscribeFromViewData(this.parentRecordStore.tableId, this.viewId);
177
+ this._queryManager.unsubscribeFromQueryId(this._queryIdFromReferenceCountedLoadData);
243
178
 
244
- if (!this.isDeleted) {
245
- this._data.visibleRecordIds = undefined;
246
- this._data.groups = undefined;
247
- this._data.colorsByRecordId = undefined;
248
- }
249
- }
250
- }, {
251
- key: "__generateChangesForParentTableAddMultipleRecords",
252
- value: function __generateChangesForParentTableAddMultipleRecords(recordIds) {
253
- var newVisibleRecordIds = [...this.visibleRecordIds, ...recordIds];
254
- return [{
255
- path: ['tablesById', this.parentRecordStore.tableId, 'viewsById', this.viewId, 'visibleRecordIds'],
256
- value: newVisibleRecordIds
257
- }];
258
- }
259
- }, {
260
- key: "__generateChangesForParentTableDeleteMultipleRecords",
261
- value: function __generateChangesForParentTableDeleteMultipleRecords(recordIds) {
262
- var recordIdsToDeleteSet = {};
263
- var _iteratorNormalCompletion2 = true;
264
- var _didIteratorError2 = false;
265
- var _iteratorError2 = undefined;
266
-
267
- try {
268
- for (var _iterator2 = recordIds[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
269
- var recordId = _step2.value;
270
- recordIdsToDeleteSet[recordId] = true;
271
- }
272
- } catch (err) {
273
- _didIteratorError2 = true;
274
- _iteratorError2 = err;
275
- } finally {
276
- try {
277
- if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
278
- _iterator2.return();
279
- }
280
- } finally {
281
- if (_didIteratorError2) {
282
- throw _iteratorError2;
283
- }
284
- }
285
- }
286
-
287
- var newVisibleRecordIds = this.visibleRecordIds.filter(recordId => !recordIdsToDeleteSet[recordId]);
288
- var changePayload = [{
289
- path: ['tablesById', this.parentRecordStore.tableId, 'viewsById', this.viewId, 'visibleRecordIds'],
290
- value: newVisibleRecordIds
291
- }];
292
-
293
- if (this._data.groups) {
294
- var newGroups = this.__recursivelyRemoveRecordsFromGroupsInPlace((0, _private_utils.cloneDeep)(this._data.groups), recordIdsToDeleteSet);
295
-
296
- changePayload.push({
297
- path: ['tablesById', this.parentRecordStore.tableId, 'viewsById', this.viewId, 'groups'],
298
- value: newGroups
299
- });
300
- }
301
-
302
- return changePayload;
303
- }
304
- }, {
305
- key: "__recursivelyRemoveRecordsFromGroupsInPlace",
306
- value: function __recursivelyRemoveRecordsFromGroupsInPlace(groups, recordIdsToDeleteSet) {
307
- if (!groups || groups.length === 0) {
308
- return groups;
309
- }
310
-
311
- return groups.map(group => {
312
- if (group.visibleRecordIds) {
313
- group.visibleRecordIds = group.visibleRecordIds.filter(id => !recordIdsToDeleteSet[id]);
314
- }
315
-
316
- this.__recursivelyRemoveRecordsFromGroupsInPlace(group.groups, recordIdsToDeleteSet);
317
-
318
- return group;
319
- });
179
+ this._queryIdFromReferenceCountedLoadData = null;
320
180
  }
321
181
  /**
322
182
  * The record IDs that are not filtered out of this view.
@@ -334,107 +194,197 @@ function (_AbstractModelWithAsy) {
334
194
  * @param recordOrRecordId the record/record id to get the color for
335
195
  */
336
196
  value: function getRecordColor(record) {
337
- var _ref3, _this$_data$colorsByR;
338
-
339
197
  (0, _error_utils.invariant)(this.isDataLoaded, 'View data is not loaded');
340
- return (_ref3 = (_this$_data$colorsByR = this._data.colorsByRecordId) === null || _this$_data$colorsByR === void 0 ? void 0 : _this$_data$colorsByR[record.id]) !== null && _ref3 !== void 0 ? _ref3 : null;
198
+
199
+ this._assertExistsAndLoaded();
200
+
201
+ return this._publicViewStore.getColorForRecordId(record.id);
341
202
  }
342
203
  }, {
343
- key: "triggerOnChangeForDirtyPaths",
344
- value: function triggerOnChangeForDirtyPaths(dirtyPaths) {
345
- if (dirtyPaths.visibleRecordIds) {
346
- this._onChange(WatchableViewDataStoreKeys.visibleRecords);
204
+ key: "_findBlockSubscriptionChangedMatchingSubscriptionChangeTypeForViewIfExists",
205
+ value: function _findBlockSubscriptionChangedMatchingSubscriptionChangeTypeForViewIfExists(changes, changeTypeToMatch) {
206
+ var _changes$find;
347
207
 
348
- this._onChange(WatchableViewDataStoreKeys.visibleRecordIds);
349
- }
208
+ return (_changes$find = changes.find(change => change.type === changeTypeToMatch && change.tableId === this.parentRecordStore.tableId && change.viewId === this.viewId)) !== null && _changes$find !== void 0 ? _changes$find : null;
209
+ }
210
+ }, {
211
+ key: "_constructBlockQuerySpecForWatchableKey",
212
+ value: function _constructBlockQuerySpecForWatchableKey(key, callbackForRegistration, context) {
213
+ var querySpecOptions = {};
214
+
215
+ var wrappedCallbackForQueryChange = () => {
216
+ callbackForRegistration.call(context, this, key);
217
+ };
218
+
219
+ switch (key) {
220
+ case WatchableViewDataStoreKeys.visibleRecords:
221
+ querySpecOptions.shouldIncludeVisibleRecordIds = true;
222
+ break;
223
+
224
+ case WatchableViewDataStoreKeys.groups:
225
+ {
226
+ querySpecOptions.shouldIncludeGroups = true;
227
+ break;
228
+ }
350
229
 
351
- if (dirtyPaths.fieldOrder) {
352
- this._onChange(WatchableViewDataStoreKeys.allFieldIds); // TODO(kasra): only trigger visibleFields if the *visible* field ids changed.
230
+ case WatchableViewDataStoreKeys.groupLevels:
231
+ {
232
+ querySpecOptions.shouldIncludeGroupingLevels = true;
233
+ break;
234
+ }
353
235
 
236
+ case WatchableViewDataStoreKeys.recordColors:
237
+ {
238
+ querySpecOptions.shouldIncludeColorsForRecordId = true;
354
239
 
355
- this._onChange(WatchableViewDataStoreKeys.visibleFieldIds);
356
- } // Technically it's possible for groupLevels changing to cause a groups
357
- // change since we derive group information from the groupLevels (fieldId)
240
+ wrappedCallbackForQueryChange = changes => {
241
+ var recordColorsChange = this._findBlockSubscriptionChangedMatchingSubscriptionChangeTypeForViewIfExists(changes, _block_query_spec.BlockQuerySubscriptionChangeType.CHANGED_COLORS_BY_RECORD_ID_FOR_VIEW);
358
242
 
243
+ var recordIdsOrNull = null;
359
244
 
360
- if (dirtyPaths.groups || dirtyPaths.groupLevels) {
361
- this._onChange(WatchableViewDataStoreKeys.groups);
362
- }
245
+ if (recordColorsChange) {
246
+ (0, _error_utils.invariant)(recordColorsChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.CHANGED_COLORS_BY_RECORD_ID_FOR_VIEW, 'Invalid record colors change event');
247
+ recordIdsOrNull = recordColorsChange.recordIds;
248
+ } // We also always call this, even if no recordColors changed to closely mirror
249
+ // the old version of the SDK (https://github.com/Hyperbase/blocks-sdk/blob/8e3ac1a37be9c9850e64085975b102a483116080/packages/sdk/src/models/view_data_store.ts#L319-L361)
250
+ // In the future, this should not be the case (don't call callback if nothing changed)
363
251
 
364
- if (dirtyPaths.groupLevels) {
365
- this._onChange(WatchableViewDataStoreKeys.groupLevels);
366
- }
367
252
 
368
- if (dirtyPaths.colorsByRecordId) {
369
- var changedRecordIds = dirtyPaths.colorsByRecordId._isDirty ? null : Object.keys(dirtyPaths.colorsByRecordId);
370
-
371
- if (changedRecordIds) {
372
- // Checking isRecordMetadataLoaded fixes a timing issue:
373
- // When a new table loads in liveapp, we'll receive the record
374
- // colors before getting the response to our loadData call.
375
- // This is a temporary fix: we need a more general solution to
376
- // avoid processing events associated with subscriptions whose
377
- // data we haven't received yet.
378
- if (this.parentRecordStore.isRecordMetadataLoaded) {
379
- var _iteratorNormalCompletion3 = true;
380
- var _didIteratorError3 = false;
381
- var _iteratorError3 = undefined;
382
-
383
- try {
384
- for (var _iterator3 = changedRecordIds[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
385
- var recordId = _step3.value;
386
- var record = this.parentRecordStore.getRecordByIdIfExists(recordId); // Similar to above, we could be receiving the change notification
387
- // for a record color before receiving the new record itself.
388
-
389
- if (record) {
390
- record.__triggerOnChangeForRecordColorInViewId(this.viewId);
391
- }
392
- }
393
- } catch (err) {
394
- _didIteratorError3 = true;
395
- _iteratorError3 = err;
396
- } finally {
397
- try {
398
- if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
399
- _iterator3.return();
400
- }
401
- } finally {
402
- if (_didIteratorError3) {
403
- throw _iteratorError3;
404
- }
405
- }
406
- }
253
+ callbackForRegistration.call(context, this, key, recordIdsOrNull);
254
+ };
255
+
256
+ break;
407
257
  }
408
- }
409
258
 
410
- this._onChange(WatchableViewDataStoreKeys.recordColors, changedRecordIds);
259
+ case WatchableViewDataStoreKeys.allFieldIds:
260
+ {
261
+ querySpecOptions.shouldIncludeFieldOrder = true;
262
+ break;
263
+ }
264
+
265
+ case WatchableViewDataStoreKeys.visibleFieldIds:
266
+ {
267
+ // TODO: This will trigger even if fieldIds which aren't visible in the view change.
268
+ // This is NOT a regression, a fix would be to add an intermediate callback
269
+ // which swallows changes if the visible fieldOrder does not change.
270
+ querySpecOptions.shouldIncludeFieldOrder = true;
271
+ break;
272
+ }
273
+ // TypeScript ensures that key is of type never so we don't need to test this via istanbul
274
+ // istanbul ignore next
275
+
276
+ default:
277
+ throw (0, _error_utils.spawnExhaustiveSwitchError)(key);
411
278
  }
279
+
280
+ return {
281
+ querySpecToSubscribeWith: {
282
+ sourceType: _block_query_spec.BlockQuerySourceType.VIEW,
283
+ sourceTableId: this.parentRecordStore.tableId,
284
+ sourceViewId: this.viewId,
285
+ viewMetadataSelection: querySpecOptions
286
+ },
287
+ wrappedCallbackForQueryChange
288
+ };
289
+ }
290
+ /**
291
+ * Watching a key that needs to load data asynchronously will automatically
292
+ * cause the data to be fetched. Once the data is available, the callback
293
+ * will be called.
294
+ *
295
+ * @inheritdoc
296
+ */
297
+
298
+ }, {
299
+ key: "watch",
300
+ value: function watch(keys, callbackForWatchKey, context) {
301
+ return this._internalWatch(_airtable_interface.BlockQueryCallbackPriority.BLOCK_CODE, keys, callbackForWatchKey, context);
302
+ }
303
+ }, {
304
+ key: "watchFromSdk",
305
+ value: function watchFromSdk(keys, callbackForWatchKey, context) {
306
+ return this._internalWatch(_airtable_interface.BlockQueryCallbackPriority.SDK, keys, callbackForWatchKey, context);
307
+ }
308
+ }, {
309
+ key: "_internalWatch",
310
+ value: function _internalWatch(priority, keys, callbackForWatchKey, context) {
311
+ this._assertNotForceUnloaded();
312
+
313
+ var validKeys = this._getWatchableValidKeysOrThrow(keys, 'view watch');
314
+
315
+ validKeys.forEach(validKey => {
316
+ var _this$_constructBlock = this._constructBlockQuerySpecForWatchableKey(validKey, callbackForWatchKey, context),
317
+ querySpecToSubscribeWith = _this$_constructBlock.querySpecToSubscribeWith,
318
+ wrappedCallbackForQueryChange = _this$_constructBlock.wrappedCallbackForQueryChange;
319
+
320
+ this._queryManager.watchWithQuerySpec(priority, querySpecToSubscribeWith, this, validKey, wrappedCallbackForQueryChange, callbackForWatchKey, context);
321
+ });
322
+ return validKeys;
323
+ }
324
+ /**
325
+ * Unwatching a key that needs to load data asynchronously will automatically
326
+ * cause the data to be released. Once the data is available, the callback
327
+ * will be called.
328
+ *
329
+ * @inheritdoc
330
+ */
331
+
332
+ }, {
333
+ key: "unwatch",
334
+ value: function unwatch(keys, callbackForRegistration, context) {
335
+ // We have already unsubscribed from all watches (because query-based watches force data to be loaded)
336
+ // so no watches are subscribed
337
+ if (this._isForceUnloaded) {
338
+ return [];
339
+ } // We warn instead of throw here because we used to warn instead of throw and don't
340
+ // want to make a breaking change.
341
+
342
+
343
+ var validKeys = this._getWatchableValidKeysOrThrow(keys, 'view unwatch', true);
344
+
345
+ validKeys.forEach(key => this._queryManager.unwatchFromQueryKey(key, callbackForRegistration, context));
346
+ return validKeys;
347
+ }
348
+ }, {
349
+ key: "triggerOnChangeForDirtyPaths",
350
+ value: function triggerOnChangeForDirtyPaths(dirtyPaths) {// This is only triggered for changes to values in the type ViewData, which is
351
+ // populated and kept up to date by watching base schema (aka the view's id, name, type).
352
+ // None of these are watchable, so we don't need to call this._onChange() for them.
353
+ // In the future we may want to make the view's name watchable, which is the only property
354
+ // here that we'd expect to change. In this case, it would be handled here.
412
355
  }
413
356
  }, {
414
357
  key: "_dataOrNullIfDeleted",
415
358
  get: function get() {
416
- var _ref4;
359
+ var _ref;
417
360
 
418
361
  var tableData = this._baseData.tablesById[this.parentRecordStore.tableId];
419
- return (_ref4 = tableData === null || tableData === void 0 ? void 0 : tableData.viewsById[this.viewId]) !== null && _ref4 !== void 0 ? _ref4 : null;
362
+ return (_ref = tableData === null || tableData === void 0 ? void 0 : tableData.viewsById[this.viewId]) !== null && _ref !== void 0 ? _ref : null;
420
363
  }
421
364
  }, {
422
365
  key: "isDataLoaded",
423
366
  get: function get() {
424
- return this._isDataLoaded && this.parentRecordStore.isRecordMetadataLoaded;
367
+ return this._isDataLoaded;
368
+ }
369
+ }, {
370
+ key: "isRecordDataLoadedForView",
371
+ get: function get() {
372
+ // We _currently_ only issue queries that also load cell data in the view which
373
+ // is synchronously returned with all other view data, so we can collapse this
374
+ // to if the viewData has loaded.
375
+ // TODO: (#proj-blocks-sdk-record-limits) Actually track if all cell data is loaded
376
+ // based on issued queries from ViewDataStore
377
+ return this.isDataLoaded;
425
378
  }
426
379
  }, {
427
380
  key: "visibleRecordIds",
428
381
  get: function get() {
429
- var visibleRecordIds = this._data.visibleRecordIds;
430
- (0, _error_utils.invariant)(visibleRecordIds, 'View data is not loaded'); // Freeze visibleRecordIds so users can't mutate it.
431
- // If it changes from liveapp, we get an entire new array which will
432
- // replace this one, so it's okay to freeze it.
382
+ this._assertExistsAndLoaded(); // Freezing the visibleRecordIds happens on the other side of the bridge
433
383
 
434
- if (!Object.isFrozen(visibleRecordIds)) {
435
- Object.freeze(visibleRecordIds);
436
- }
437
384
 
385
+ var visibleRecordIds = this._publicViewStore.getVisibleRecordIds();
386
+
387
+ (0, _error_utils.invariant)(visibleRecordIds, 'View data is not loaded');
438
388
  return visibleRecordIds;
439
389
  }
440
390
  /**
@@ -446,8 +396,12 @@ function (_AbstractModelWithAsy) {
446
396
  }, {
447
397
  key: "visibleRecords",
448
398
  get: function get() {
449
- (0, _error_utils.invariant)(this.parentRecordStore.isRecordMetadataLoaded, 'Table data is not loaded');
450
- var visibleRecordIds = this._data.visibleRecordIds;
399
+ (0, _error_utils.invariant)(this.isRecordDataLoadedForView, 'Table data is not loaded for this view');
400
+
401
+ this._assertExistsAndLoaded();
402
+
403
+ var visibleRecordIds = this._publicViewStore.getVisibleRecordIds();
404
+
451
405
  (0, _error_utils.invariant)(visibleRecordIds, 'View data is not loaded');
452
406
  return visibleRecordIds.map(recordId => {
453
407
  var record = this.parentRecordStore.getRecordByIdIfExists(recordId);
@@ -466,9 +420,9 @@ function (_AbstractModelWithAsy) {
466
420
  }, {
467
421
  key: "groups",
468
422
  get: function get() {
469
- (0, _error_utils.invariant)(this.parentRecordStore.isRecordMetadataLoaded, 'Table data is not loaded');
470
- var groups = this._data.groups;
471
- return groups !== null && groups !== void 0 ? groups : null;
423
+ this._assertExistsAndLoaded();
424
+
425
+ return this._publicViewStore.getGroups();
472
426
  }
473
427
  /**
474
428
  * Gets the group config for this view, can be watched to know when groupLevels
@@ -478,23 +432,29 @@ function (_AbstractModelWithAsy) {
478
432
  }, {
479
433
  key: "groupLevels",
480
434
  get: function get() {
481
- (0, _error_utils.invariant)(this.parentRecordStore.isRecordMetadataLoaded, 'Table data is not loaded');
482
- var groupLevels = this._data.groupLevels;
483
- return groupLevels !== null && groupLevels !== void 0 ? groupLevels : null;
435
+ this._assertExistsAndLoaded();
436
+
437
+ return this._publicViewStore.getGroupLevels();
484
438
  }
485
439
  }, {
486
440
  key: "allFieldIds",
487
441
  get: function get() {
488
- var fieldOrder = this._data.fieldOrder;
489
- (0, _error_utils.invariant)(fieldOrder, 'View data is not loaded');
490
- return fieldOrder.fieldIds;
442
+ this._assertExistsAndLoaded(); // TODO: (#proj-blocks-sdk-record-limits) Future sdk version shouldn't deep
443
+ // copy this, and it should be legal to return the frozen object directly
444
+
445
+
446
+ return (0, _private_utils.cloneDeep)(this._publicViewStore.getFieldOrder().fieldIds);
491
447
  }
492
448
  }, {
493
449
  key: "visibleFieldIds",
494
450
  get: function get() {
495
- var fieldOrder = this._data.fieldOrder;
451
+ this._assertExistsAndLoaded();
452
+
453
+ var fieldOrder = this._publicViewStore.getFieldOrder();
454
+
496
455
  (0, _error_utils.invariant)(fieldOrder, 'View data is not loaded');
497
- var fieldIds = fieldOrder.fieldIds;
456
+ var fieldIds = fieldOrder.fieldIds; // Slice returns a mutable copy even though visibleFieldCount is readonly
457
+
498
458
  return fieldIds.slice(0, fieldOrder.visibleFieldCount);
499
459
  }
500
460
  }]);