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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/dist/cjs/error_utils.js +9 -34
  3. package/dist/cjs/models/base.js +15 -0
  4. package/dist/cjs/models/field.js +99 -4
  5. package/dist/cjs/models/grouped_record_query_result.js +14 -5
  6. package/dist/cjs/models/linked_records_query_result.js +44 -86
  7. package/dist/cjs/models/mutations.js +234 -53
  8. package/dist/cjs/models/record.js +55 -310
  9. package/dist/cjs/models/record_query_result.js +1 -4
  10. package/dist/cjs/models/record_store.js +779 -557
  11. package/dist/cjs/models/table.js +6 -6
  12. package/dist/cjs/models/table_or_view_query_result.js +419 -526
  13. package/dist/cjs/models/view_data_store.js +295 -255
  14. package/dist/cjs/private_utils.js +0 -40
  15. package/dist/cjs/sdk.js +2 -12
  16. package/dist/cjs/testing/abstract_mock_airtable_interface.js +11 -57
  17. package/dist/cjs/types/airtable_interface.js +2 -17
  18. package/dist/cjs/types/mutations.js +1 -0
  19. package/dist/cjs/ui/use_records.js +1 -5
  20. package/dist/cjs/unstable_testing_utils.js +1 -55
  21. package/dist/cjs/watchable.js +1 -15
  22. package/dist/types/src/error_utils.d.ts +4 -2
  23. package/dist/types/src/error_utils.d.ts.map +1 -1
  24. package/dist/types/src/models/base.d.ts +10 -0
  25. package/dist/types/src/models/base.d.ts.map +1 -1
  26. package/dist/types/src/models/field.d.ts +58 -0
  27. package/dist/types/src/models/field.d.ts.map +1 -1
  28. package/dist/types/src/models/grouped_record_query_result.d.ts +3 -3
  29. package/dist/types/src/models/grouped_record_query_result.d.ts.map +1 -1
  30. package/dist/types/src/models/linked_records_query_result.d.ts.map +1 -1
  31. package/dist/types/src/models/mutations.d.ts.map +1 -1
  32. package/dist/types/src/models/record.d.ts +3 -12
  33. package/dist/types/src/models/record.d.ts.map +1 -1
  34. package/dist/types/src/models/record_query_result.d.ts +2 -3
  35. package/dist/types/src/models/record_query_result.d.ts.map +1 -1
  36. package/dist/types/src/models/record_store.d.ts.map +1 -1
  37. package/dist/types/src/models/table_or_view_query_result.d.ts +5 -3
  38. package/dist/types/src/models/table_or_view_query_result.d.ts.map +1 -1
  39. package/dist/types/src/models/view_data_store.d.ts +1 -0
  40. package/dist/types/src/models/view_data_store.d.ts.map +1 -1
  41. package/dist/types/src/models/view_metadata_query_result.d.ts +1 -1
  42. package/dist/types/src/models/view_metadata_query_result.d.ts.map +1 -1
  43. package/dist/types/src/private_utils.d.ts +1 -24
  44. package/dist/types/src/private_utils.d.ts.map +1 -1
  45. package/dist/types/src/sdk.d.ts.map +1 -1
  46. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts +11 -11
  47. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts.map +1 -1
  48. package/dist/types/src/types/airtable_interface.d.ts +20 -59
  49. package/dist/types/src/types/airtable_interface.d.ts.map +1 -1
  50. package/dist/types/src/types/base.d.ts +1 -0
  51. package/dist/types/src/types/base.d.ts.map +1 -1
  52. package/dist/types/src/types/mutations.d.ts +25 -2
  53. package/dist/types/src/types/mutations.d.ts.map +1 -1
  54. package/dist/types/src/types/table.d.ts +2 -0
  55. package/dist/types/src/types/table.d.ts.map +1 -1
  56. package/dist/types/src/types/view.d.ts +8 -3
  57. package/dist/types/src/types/view.d.ts.map +1 -1
  58. package/dist/types/src/ui/record_card.d.ts +1 -1
  59. package/dist/types/src/unstable_testing_utils.d.ts +1 -4
  60. package/dist/types/src/unstable_testing_utils.d.ts.map +1 -1
  61. package/dist/types/src/watchable.d.ts.map +1 -1
  62. package/dist/types/{src/testing → test/airtable_interface_mocks}/fixture_data.d.ts +19 -42
  63. package/dist/types/test/airtable_interface_mocks/fixture_data.d.ts.map +1 -0
  64. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts +1 -1
  65. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts.map +1 -1
  66. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts +12 -19
  67. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts.map +1 -1
  68. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts +1 -1
  69. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts.map +1 -1
  70. package/dist/types/test/test_helpers.d.ts +0 -2
  71. package/dist/types/test/test_helpers.d.ts.map +1 -1
  72. package/package.json +1 -2
  73. package/dist/cjs/models/query_manager.js +0 -328
  74. package/dist/cjs/testing/fixture_data.js +0 -268
  75. package/dist/cjs/testing/mock_base_data_stores.js +0 -876
  76. package/dist/cjs/types/block_query_spec.js +0 -85
  77. package/dist/types/src/models/query_manager.d.ts +0 -2
  78. package/dist/types/src/models/query_manager.d.ts.map +0 -1
  79. package/dist/types/src/testing/fixture_data.d.ts.map +0 -1
  80. package/dist/types/src/testing/mock_base_data_stores.d.ts +0 -55
  81. package/dist/types/src/testing/mock_base_data_stores.d.ts.map +0 -1
  82. package/dist/types/src/types/block_query_spec.d.ts +0 -139
  83. package/dist/types/src/types/block_query_spec.d.ts.map +0 -1
  84. package/dist/types/test/testing/fixture_data.test.d.ts +0 -2
  85. package/dist/types/test/testing/fixture_data.test.d.ts.map +0 -1
@@ -2,7 +2,13 @@
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
4
 
5
- require("core-js/modules/es.array.find");
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");
6
12
 
7
13
  require("core-js/modules/es.array.map");
8
14
 
@@ -10,17 +16,17 @@ require("core-js/modules/es.array.slice");
10
16
 
11
17
  require("core-js/modules/es.object.to-string");
12
18
 
13
- require("core-js/modules/es.object.values");
14
-
15
19
  require("core-js/modules/es.promise");
16
20
 
17
- require("core-js/modules/web.dom-collections.for-each");
21
+ require("core-js/modules/web.dom-collections.iterator");
18
22
 
19
23
  Object.defineProperty(exports, "__esModule", {
20
24
  value: true
21
25
  });
22
26
  exports.default = exports.WatchableViewDataStoreKeys = void 0;
23
27
 
28
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
29
+
24
30
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
25
31
 
26
32
  require("regenerator-runtime/runtime");
@@ -29,9 +35,11 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
29
35
 
30
36
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
31
37
 
38
+ var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
39
+
32
40
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
33
41
 
34
- var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
42
+ var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
35
43
 
36
44
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
37
45
 
@@ -43,14 +51,11 @@ var _private_utils = require("../private_utils");
43
51
 
44
52
  var _error_utils = require("../error_utils");
45
53
 
46
- var _airtable_interface = require("../types/airtable_interface");
47
-
48
- var _block_query_spec = require("../types/block_query_spec");
49
-
50
54
  var _abstract_model_with_async_data = _interopRequireDefault(require("./abstract_model_with_async_data"));
51
55
 
52
56
  var WatchableViewDataStoreKeys = Object.freeze({
53
57
  visibleRecords: 'visibleRecords',
58
+ visibleRecordIds: 'visibleRecordIds',
54
59
  groups: 'groups',
55
60
  groupLevels: 'groupLevels',
56
61
  recordColors: 'recordColors',
@@ -61,11 +66,9 @@ var WatchableViewDataStoreKeys = Object.freeze({
61
66
 
62
67
  exports.WatchableViewDataStoreKeys = WatchableViewDataStoreKeys;
63
68
 
64
- // ViewDataStore manages loadable data for a specific view. That means the set of visible records,
69
+ // ViewDataStore contains loadable data for a specific view. That means the set of visible records,
65
70
  // and field order/visibility information. View itself only contains core schema information. The
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
71
+ // data here doesn't belong in View as it's record data or conditionally loaded.
69
72
 
70
73
  /** @internal */
71
74
  var ViewDataStore =
@@ -77,6 +80,11 @@ function (_AbstractModelWithAsy) {
77
80
  value: function _isWatchableKey(key) {
78
81
  return (0, _private_utils.isEnumValue)(WatchableViewDataStoreKeys, key);
79
82
  }
83
+ }, {
84
+ key: "_shouldLoadDataForKey",
85
+ value: function _shouldLoadDataForKey(key) {
86
+ return true;
87
+ }
80
88
  }]);
81
89
 
82
90
  function ViewDataStore(sdk, parentRecordStore, viewId) {
@@ -86,38 +94,16 @@ function (_AbstractModelWithAsy) {
86
94
  _this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(ViewDataStore).call(this, sdk, "".concat(viewId, "-ViewDataStore")));
87
95
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "viewId", void 0);
88
96
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "parentRecordStore", 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);
97
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_mostRecentTableLoadPromise", void 0);
98
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_airtableInterface", void 0);
92
99
  _this.parentRecordStore = parentRecordStore;
93
- _this._queryManager = sdk._queryManager;
100
+ _this._airtableInterface = sdk.__airtableInterface;
101
+ _this._mostRecentTableLoadPromise = null;
94
102
  _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;
100
103
  return _this;
101
104
  }
102
105
 
103
106
  (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
- }, {
121
107
  key: "_onChangeIsDataLoaded",
122
108
  value: function _onChangeIsDataLoaded() {// noop
123
109
  }
@@ -125,58 +111,212 @@ function (_AbstractModelWithAsy) {
125
111
  key: "__onDataDeletion",
126
112
  value: function __onDataDeletion() {
127
113
  this._forceUnload();
128
-
129
- this._queryManager.forceUnloadWatchesOnWatchableModel(this);
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);
130
139
  }
131
140
  }, {
132
141
  key: "_loadDataAsync",
133
142
  value: function _loadDataAsync() {
134
- var changedKeysOnLoad;
135
- return _regenerator.default.async(function _loadDataAsync$(_context) {
143
+ var tableLoadPromise, _ref, _ref2, viewData, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, record;
144
+
145
+ return _regenerator.default.async(function _loadDataAsync$(_context2) {
136
146
  while (1) {
137
- switch (_context.prev = _context.next) {
147
+ switch (_context2.prev = _context2.next) {
138
148
  case 0:
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
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);
154
181
  }
155
- }));
182
+ }
156
183
 
157
- case 3:
158
- this._queryIdFromReferenceCountedLoadData = _context.sent;
159
- changedKeysOnLoad = Object.values(WatchableViewDataStoreKeys);
184
+ _context2.next = 24;
185
+ break;
160
186
 
161
- this._queryManager.onLoadKeys(this, changedKeysOnLoad);
187
+ case 20:
188
+ _context2.prev = 20;
189
+ _context2.t0 = _context2["catch"](16);
190
+ _didIteratorError = true;
191
+ _iteratorError = _context2.t0;
162
192
 
163
- return _context.abrupt("return", []);
193
+ case 24:
194
+ _context2.prev = 24;
195
+ _context2.prev = 25;
164
196
 
165
- case 7:
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:
166
221
  case "end":
167
- return _context.stop();
222
+ return _context2.stop();
168
223
  }
169
224
  }
170
- }, null, this);
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();
171
236
  }
172
237
  }, {
173
238
  key: "_unloadData",
174
239
  value: function _unloadData() {
175
- (0, _error_utils.invariant)(this._queryIdFromReferenceCountedLoadData, 'Can not unsubscribe from a view store that is not subscribed');
240
+ this._mostRecentTableLoadPromise = null;
176
241
 
177
- this._queryManager.unsubscribeFromQueryId(this._queryIdFromReferenceCountedLoadData);
242
+ this._airtableInterface.unsubscribeFromViewData(this.parentRecordStore.tableId, this.viewId);
178
243
 
179
- this._queryIdFromReferenceCountedLoadData = null;
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
+ });
180
320
  }
181
321
  /**
182
322
  * The record IDs that are not filtered out of this view.
@@ -194,197 +334,107 @@ function (_AbstractModelWithAsy) {
194
334
  * @param recordOrRecordId the record/record id to get the color for
195
335
  */
196
336
  value: function getRecordColor(record) {
197
- (0, _error_utils.invariant)(this.isDataLoaded, 'View data is not loaded');
198
-
199
- this._assertExistsAndLoaded();
337
+ var _ref3, _this$_data$colorsByR;
200
338
 
201
- return this._publicViewStore.getColorForRecordId(record.id);
202
- }
203
- }, {
204
- key: "_findBlockSubscriptionChangedMatchingSubscriptionChangeTypeForViewIfExists",
205
- value: function _findBlockSubscriptionChangedMatchingSubscriptionChangeTypeForViewIfExists(changes, changeTypeToMatch) {
206
- var _changes$find;
207
-
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;
339
+ (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;
209
341
  }
210
342
  }, {
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
- }
229
-
230
- case WatchableViewDataStoreKeys.groupLevels:
231
- {
232
- querySpecOptions.shouldIncludeGroupingLevels = true;
233
- break;
234
- }
235
-
236
- case WatchableViewDataStoreKeys.recordColors:
237
- {
238
- querySpecOptions.shouldIncludeColorsForRecordId = true;
343
+ key: "triggerOnChangeForDirtyPaths",
344
+ value: function triggerOnChangeForDirtyPaths(dirtyPaths) {
345
+ if (dirtyPaths.visibleRecordIds) {
346
+ this._onChange(WatchableViewDataStoreKeys.visibleRecords);
239
347
 
240
- wrappedCallbackForQueryChange = changes => {
241
- var recordColorsChange = this._findBlockSubscriptionChangedMatchingSubscriptionChangeTypeForViewIfExists(changes, _block_query_spec.BlockQuerySubscriptionChangeType.CHANGED_COLORS_BY_RECORD_ID_FOR_VIEW);
348
+ this._onChange(WatchableViewDataStoreKeys.visibleRecordIds);
349
+ }
242
350
 
243
- var recordIdsOrNull = null;
351
+ if (dirtyPaths.fieldOrder) {
352
+ this._onChange(WatchableViewDataStoreKeys.allFieldIds); // TODO(kasra): only trigger visibleFields if the *visible* field ids changed.
244
353
 
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)
251
354
 
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)
252
358
 
253
- callbackForRegistration.call(context, this, key, recordIdsOrNull);
254
- };
255
359
 
256
- break;
257
- }
360
+ if (dirtyPaths.groups || dirtyPaths.groupLevels) {
361
+ this._onChange(WatchableViewDataStoreKeys.groups);
362
+ }
258
363
 
259
- case WatchableViewDataStoreKeys.allFieldIds:
260
- {
261
- querySpecOptions.shouldIncludeFieldOrder = true;
262
- break;
263
- }
364
+ if (dirtyPaths.groupLevels) {
365
+ this._onChange(WatchableViewDataStoreKeys.groupLevels);
366
+ }
264
367
 
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;
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
+ }
272
407
  }
273
- // TypeScript ensures that key is of type never so we don't need to test this via istanbul
274
- // istanbul ignore next
408
+ }
275
409
 
276
- default:
277
- throw (0, _error_utils.spawnExhaustiveSwitchError)(key);
410
+ this._onChange(WatchableViewDataStoreKeys.recordColors, changedRecordIds);
278
411
  }
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.
355
412
  }
356
413
  }, {
357
414
  key: "_dataOrNullIfDeleted",
358
415
  get: function get() {
359
- var _ref;
416
+ var _ref4;
360
417
 
361
418
  var tableData = this._baseData.tablesById[this.parentRecordStore.tableId];
362
- return (_ref = tableData === null || tableData === void 0 ? void 0 : tableData.viewsById[this.viewId]) !== null && _ref !== void 0 ? _ref : null;
419
+ return (_ref4 = tableData === null || tableData === void 0 ? void 0 : tableData.viewsById[this.viewId]) !== null && _ref4 !== void 0 ? _ref4 : null;
363
420
  }
364
421
  }, {
365
422
  key: "isDataLoaded",
366
423
  get: function get() {
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;
424
+ return this._isDataLoaded && this.parentRecordStore.isRecordMetadataLoaded;
378
425
  }
379
426
  }, {
380
427
  key: "visibleRecordIds",
381
428
  get: function get() {
382
- this._assertExistsAndLoaded(); // Freezing the visibleRecordIds happens on the other side of the bridge
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.
383
433
 
434
+ if (!Object.isFrozen(visibleRecordIds)) {
435
+ Object.freeze(visibleRecordIds);
436
+ }
384
437
 
385
- var visibleRecordIds = this._publicViewStore.getVisibleRecordIds();
386
-
387
- (0, _error_utils.invariant)(visibleRecordIds, 'View data is not loaded');
388
438
  return visibleRecordIds;
389
439
  }
390
440
  /**
@@ -396,12 +446,8 @@ function (_AbstractModelWithAsy) {
396
446
  }, {
397
447
  key: "visibleRecords",
398
448
  get: function get() {
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
-
449
+ (0, _error_utils.invariant)(this.parentRecordStore.isRecordMetadataLoaded, 'Table data is not loaded');
450
+ var visibleRecordIds = this._data.visibleRecordIds;
405
451
  (0, _error_utils.invariant)(visibleRecordIds, 'View data is not loaded');
406
452
  return visibleRecordIds.map(recordId => {
407
453
  var record = this.parentRecordStore.getRecordByIdIfExists(recordId);
@@ -420,9 +466,9 @@ function (_AbstractModelWithAsy) {
420
466
  }, {
421
467
  key: "groups",
422
468
  get: function get() {
423
- this._assertExistsAndLoaded();
424
-
425
- return this._publicViewStore.getGroups();
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;
426
472
  }
427
473
  /**
428
474
  * Gets the group config for this view, can be watched to know when groupLevels
@@ -432,29 +478,23 @@ function (_AbstractModelWithAsy) {
432
478
  }, {
433
479
  key: "groupLevels",
434
480
  get: function get() {
435
- this._assertExistsAndLoaded();
436
-
437
- return this._publicViewStore.getGroupLevels();
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;
438
484
  }
439
485
  }, {
440
486
  key: "allFieldIds",
441
487
  get: function get() {
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);
488
+ var fieldOrder = this._data.fieldOrder;
489
+ (0, _error_utils.invariant)(fieldOrder, 'View data is not loaded');
490
+ return fieldOrder.fieldIds;
447
491
  }
448
492
  }, {
449
493
  key: "visibleFieldIds",
450
494
  get: function get() {
451
- this._assertExistsAndLoaded();
452
-
453
- var fieldOrder = this._publicViewStore.getFieldOrder();
454
-
495
+ var fieldOrder = this._data.fieldOrder;
455
496
  (0, _error_utils.invariant)(fieldOrder, 'View data is not loaded');
456
- var fieldIds = fieldOrder.fieldIds; // Slice returns a mutable copy even though visibleFieldCount is readonly
457
-
497
+ var fieldIds = fieldOrder.fieldIds;
458
498
  return fieldIds.slice(0, fieldOrder.visibleFieldCount);
459
499
  }
460
500
  }]);