cozy-client 32.3.3 → 32.5.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.
@@ -140,6 +140,7 @@ var DOC_UPDATE = 'update';
140
140
  * @property {object} [stackClient]
141
141
  * @property {boolean} [warningForCustomHandlers]
142
142
  * @property {boolean} [autoHydrate]
143
+ * @property {boolean} [backgroundFetching] - If set to true, backgroundFetching will be enabled by default on every query. Meaning that, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
143
144
  * @property {object} [oauth]
144
145
  * @property {Function} [onTokenRefresh]
145
146
  * @property {Function} [onError] - Default callback if a query is errored
@@ -1233,6 +1234,7 @@ var CozyClient = /*#__PURE__*/function () {
1233
1234
  queryId,
1234
1235
  existingQuery,
1235
1236
  shouldFetch,
1237
+ backgroundFetching,
1236
1238
  response,
1237
1239
  _args8 = arguments;
1238
1240
 
@@ -1286,55 +1288,59 @@ var CozyClient = /*#__PURE__*/function () {
1286
1288
  case 14:
1287
1289
  this.ensureQueryExists(queryId, queryDefinition, options);
1288
1290
  _context8.prev = 15;
1289
- this.dispatch((0, _store.loadQuery)(queryId));
1290
- _context8.next = 19;
1291
+ backgroundFetching = options.backgroundFetching !== undefined ? options.backgroundFetching : this.options.backgroundFetching;
1292
+ this.dispatch((0, _store.loadQuery)(queryId, {
1293
+ backgroundFetching: backgroundFetching
1294
+ }));
1295
+ _context8.next = 20;
1291
1296
  return this._promiseCache.exec(function () {
1292
1297
  return _this4.requestQuery(queryDefinition);
1293
1298
  }, function () {
1294
1299
  return (0, _jsonStableStringify.default)(queryDefinition);
1295
1300
  });
1296
1301
 
1297
- case 19:
1302
+ case 20:
1298
1303
  response = _context8.sent;
1299
1304
  this.dispatch((0, _store.receiveQueryResult)(queryId, response, {
1300
- update: update
1305
+ update: update,
1306
+ backgroundFetching: backgroundFetching
1301
1307
  }));
1302
1308
  return _context8.abrupt("return", response);
1303
1309
 
1304
- case 24:
1305
- _context8.prev = 24;
1310
+ case 25:
1311
+ _context8.prev = 25;
1306
1312
  _context8.t0 = _context8["catch"](15);
1307
1313
  this.dispatch((0, _store.receiveQueryError)(queryId, _context8.t0)); // specific onError
1308
1314
 
1309
1315
  if (!options.onError) {
1310
- _context8.next = 31;
1316
+ _context8.next = 32;
1311
1317
  break;
1312
1318
  }
1313
1319
 
1314
1320
  options.onError(_context8.t0); // defaulted onError
1315
1321
 
1316
- _context8.next = 36;
1322
+ _context8.next = 37;
1317
1323
  break;
1318
1324
 
1319
- case 31:
1325
+ case 32:
1320
1326
  if (!this.options.onError) {
1321
- _context8.next = 35;
1327
+ _context8.next = 36;
1322
1328
  break;
1323
1329
  }
1324
1330
 
1325
1331
  this.options.onError(_context8.t0);
1326
- _context8.next = 36;
1332
+ _context8.next = 37;
1327
1333
  break;
1328
1334
 
1329
- case 35:
1335
+ case 36:
1330
1336
  throw _context8.t0;
1331
1337
 
1332
- case 36:
1338
+ case 37:
1333
1339
  case "end":
1334
1340
  return _context8.stop();
1335
1341
  }
1336
1342
  }
1337
- }, _callee8, this, [[15, 24]]);
1343
+ }, _callee8, this, [[15, 25]]);
1338
1344
  }));
1339
1345
 
1340
1346
  function query(_x9) {
@@ -2565,7 +2571,7 @@ var CozyClient = /*#__PURE__*/function () {
2565
2571
  CozyClient.hooks = CozyClient.hooks || {};
2566
2572
  CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
2567
2573
 
2568
- CozyClient.version = "32.3.3";
2574
+ CozyClient.version = "32.5.0";
2569
2575
 
2570
2576
  _microee.default.mixin(CozyClient);
2571
2577
 
@@ -28,7 +28,8 @@ import {
28
28
  receiveMutationResult,
29
29
  receiveMutationError,
30
30
  getQueryFromState,
31
- getRawQueryFromState
31
+ getRawQueryFromState,
32
+ loadQuery
32
33
  } from './store'
33
34
  import { HasManyFiles, Association, HasMany } from './associations'
34
35
  import mapValues from 'lodash/mapValues'
@@ -1417,6 +1418,44 @@ describe('CozyClient', () => {
1417
1418
  )
1418
1419
  })
1419
1420
 
1421
+ it('should dispatch a LOAD_QUERY action', async () => {
1422
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1423
+ getQueryFromState.mockReturnValueOnce({
1424
+ fetchStatus: 'loaded'
1425
+ })
1426
+ await client.query(query, { as: 'allTodos' })
1427
+ expect(client.store.dispatch.mock.calls[1][0]).toEqual(
1428
+ loadQuery('allTodos', {})
1429
+ )
1430
+ })
1431
+
1432
+ it('should dispatch a LOAD_QUERY action providing backgroundFetching when set', async () => {
1433
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1434
+ getQueryFromState.mockReturnValueOnce({
1435
+ fetchStatus: 'loaded'
1436
+ })
1437
+ await client.query(query, { as: 'allTodos', backgroundFetching: true })
1438
+ expect(client.store.dispatch.mock.calls[1][0]).toEqual(
1439
+ loadQuery('allTodos', { backgroundFetching: true })
1440
+ )
1441
+ })
1442
+
1443
+ it('should dispatch a RECEIVE_QUERY_RESULT action providing backgroundFetching when set', async () => {
1444
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1445
+ getQueryFromState.mockReturnValueOnce({
1446
+ fetchStatus: 'loaded'
1447
+ })
1448
+ query.skip = 100
1449
+ await client.query(query, { as: 'allTodos', backgroundFetching: true })
1450
+ const dispatchCalls = client.store.dispatch.mock.calls
1451
+ const lastDispatchCall = dispatchCalls[dispatchCalls.length - 1]
1452
+ expect(lastDispatchCall[0]).toEqual(
1453
+ receiveQueryResult('allTodos', fakeResponse, {
1454
+ backgroundFetching: true
1455
+ })
1456
+ )
1457
+ })
1458
+
1420
1459
  it('should dispatch a RECEIVE_QUERY_RESULT action if query has skip', async () => {
1421
1460
  requestHandler.mockResolvedValueOnce(fakeResponse)
1422
1461
  getQueryFromState.mockReturnValueOnce({
@@ -192,6 +192,7 @@ describe('Query', () => {
192
192
  fetchStatus: 'pending',
193
193
  hasMore: false,
194
194
  id: '1',
195
+ isFetching: null,
195
196
  lastError: null,
196
197
  lastFetch: null,
197
198
  lastUpdate: null,
package/dist/helpers.js CHANGED
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.rootCozyUrl = exports.InvalidCozyUrlError = exports.InvalidProtocolError = exports.generateWebLink = exports.dehydrate = void 0;
8
+ exports.rootCozyUrl = exports.InvalidCozyUrlError = exports.InvalidProtocolError = exports.deconstructCozyWebLinkWithSlug = exports.generateWebLink = exports.dehydrate = void 0;
9
9
 
10
10
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
11
 
@@ -29,6 +29,8 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
29
29
 
30
30
  var _associations = require("./associations");
31
31
 
32
+ var _types = require("./types");
33
+
32
34
  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
33
35
 
34
36
  function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
@@ -124,9 +126,38 @@ var generateWebLink = function generateWebLink(_ref3) {
124
126
 
125
127
  return url.toString();
126
128
  };
129
+ /**
130
+ * Deconstruct the given link in order to retrieve useful data like Cozy's name, domain, or slug
131
+ *
132
+ * The given link MUST contain a slug
133
+ *
134
+ * @param {string} webLink - link to deconstruct. It should be a link from a Cozy and containing a slug
135
+ * @param {SubdomainType} [subDomainType=flat] - whether the cozy is using flat or nested subdomains.
136
+ * @returns {CozyLinkData} Deconstructed link
137
+ */
138
+
127
139
 
128
140
  exports.generateWebLink = generateWebLink;
129
141
 
142
+ var deconstructCozyWebLinkWithSlug = function deconstructCozyWebLinkWithSlug(webLink) {
143
+ var subDomainType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'flat';
144
+ var url = new URL(webLink);
145
+ var slug = subDomainType === 'nested' ? url.host.split('.')[0] : url.host.split('.')[0].split('-')[1];
146
+ var cozyName = subDomainType === 'nested' ? url.host.split('.')[1] : url.host.split('.')[0].split('-')[0];
147
+ var cozyBaseDomain = url.host.split('.').slice(subDomainType === 'nested' ? 2 : 1).join('.');
148
+ return {
149
+ cozyBaseDomain: cozyBaseDomain,
150
+ cozyName: cozyName,
151
+ pathname: url.pathname,
152
+ hash: url.hash,
153
+ protocol: url.protocol,
154
+ searchParams: url.searchParams.toString(),
155
+ slug: slug
156
+ };
157
+ };
158
+
159
+ exports.deconstructCozyWebLinkWithSlug = deconstructCozyWebLinkWithSlug;
160
+
130
161
  var InvalidProtocolError = /*#__PURE__*/function (_Error) {
131
162
  (0, _inherits2.default)(InvalidProtocolError, _Error);
132
163
 
@@ -1,6 +1,7 @@
1
1
  import { enableFetchMocks, disableFetchMocks } from 'jest-fetch-mock'
2
2
 
3
3
  import {
4
+ deconstructCozyWebLinkWithSlug,
4
5
  dehydrate,
5
6
  generateWebLink,
6
7
  rootCozyUrl,
@@ -150,6 +151,83 @@ describe('generateWebLink', () => {
150
151
  })
151
152
  })
152
153
 
154
+ describe('deconstructWebLink', () => {
155
+ it.each([
156
+ [
157
+ 'https://claude-notes.mycozy.cloud',
158
+ 'flat',
159
+ {
160
+ protocol: 'https:',
161
+ cozyName: 'claude',
162
+ cozyBaseDomain: 'mycozy.cloud',
163
+ pathname: '/',
164
+ hash: '',
165
+ searchParams: '',
166
+ slug: 'notes'
167
+ }
168
+ ],
169
+ [
170
+ 'http://alice-drive.cozy.tools:8080',
171
+ 'flat',
172
+ {
173
+ protocol: 'http:',
174
+ cozyName: 'alice',
175
+ cozyBaseDomain: 'cozy.tools:8080',
176
+ pathname: '/',
177
+ hash: '',
178
+ searchParams: '',
179
+ slug: 'drive'
180
+ }
181
+ ],
182
+ [
183
+ 'https://photo.claude.custome-domain.fr',
184
+ 'nested',
185
+ {
186
+ protocol: 'https:',
187
+ cozyName: 'claude',
188
+ cozyBaseDomain: 'custome-domain.fr',
189
+ pathname: '/',
190
+ hash: '',
191
+ searchParams: '',
192
+ slug: 'photo'
193
+ }
194
+ ],
195
+ [
196
+ 'http://drive.cozy.tools:8080/#/folder/SOME_FOLDER_ID',
197
+ 'nested',
198
+ {
199
+ protocol: 'http:',
200
+ cozyName: 'cozy',
201
+ cozyBaseDomain: 'tools:8080',
202
+ pathname: '/',
203
+ hash: '#/folder/SOME_FOLDER_ID',
204
+ searchParams: '',
205
+ slug: 'drive'
206
+ }
207
+ ],
208
+ [
209
+ 'http://notes.cozy.tools:8080/public/?id=SOME_FOLDER_ID&sharecode=SOME_SHARECODE',
210
+ 'nested',
211
+ {
212
+ protocol: 'http:',
213
+ cozyName: 'cozy',
214
+ cozyBaseDomain: 'tools:8080',
215
+ pathname: '/public/',
216
+ hash: '',
217
+ searchParams: 'id=SOME_FOLDER_ID&sharecode=SOME_SHARECODE',
218
+ slug: 'notes'
219
+ }
220
+ ]
221
+ ])(
222
+ 'should deconstruct %p link with %p subdomain type',
223
+ (link, subdomainType, result) => {
224
+ expect(deconstructCozyWebLinkWithSlug(link, subdomainType)).toEqual(
225
+ result
226
+ )
227
+ }
228
+ )
229
+ })
230
+
153
231
  describe('rootCozyUrl', () => {
154
232
  beforeAll(() => {
155
233
  enableFetchMocks()
@@ -78,6 +78,7 @@ describe('use query', () => {
78
78
  'id',
79
79
  'definition',
80
80
  'fetchStatus',
81
+ 'isFetching',
81
82
  'lastFetch',
82
83
  'lastUpdate',
83
84
  'lastErrorUpdate',
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ var _exportNames = {
26
26
  isReferencedById: true,
27
27
  getReferencedBy: true,
28
28
  getReferencedById: true,
29
+ deconstructCozyWebLinkWithSlug: true,
29
30
  dehydrate: true,
30
31
  generateWebLink: true,
31
32
  rootCozyUrl: true,
@@ -163,6 +164,12 @@ Object.defineProperty(exports, "getReferencedById", {
163
164
  return _helpers.getReferencedById;
164
165
  }
165
166
  });
167
+ Object.defineProperty(exports, "deconstructCozyWebLinkWithSlug", {
168
+ enumerable: true,
169
+ get: function get() {
170
+ return _helpers2.deconstructCozyWebLinkWithSlug;
171
+ }
172
+ });
166
173
  Object.defineProperty(exports, "dehydrate", {
167
174
  enumerable: true,
168
175
  get: function get() {
@@ -1,6 +1,6 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`queries reducer should init correctly 1`] = `
3
+ exports[`queries reducer INIT_QUERY should init correctly 1`] = `
4
4
  Object {
5
5
  "a": Object {
6
6
  "bookmark": null,
@@ -26,6 +26,7 @@ Object {
26
26
  "fetchedPagesCount": 0,
27
27
  "hasMore": false,
28
28
  "id": "a",
29
+ "isFetching": null,
29
30
  "lastError": null,
30
31
  "lastErrorUpdate": null,
31
32
  "lastFetch": null,
@@ -64,6 +65,7 @@ Object {
64
65
  "fetchedPagesCount": 1,
65
66
  "hasMore": false,
66
67
  "id": "a",
68
+ "isFetching": null,
67
69
  "lastError": null,
68
70
  "lastErrorUpdate": null,
69
71
  "lastFetch": 1337,
@@ -101,6 +103,7 @@ Object {
101
103
  "fetchedPagesCount": 1,
102
104
  "hasMore": false,
103
105
  "id": "a",
106
+ "isFetching": null,
104
107
  "lastError": null,
105
108
  "lastErrorUpdate": null,
106
109
  "lastFetch": 1337,
@@ -138,6 +141,7 @@ Object {
138
141
  "fetchedPagesCount": 0,
139
142
  "hasMore": false,
140
143
  "id": "b",
144
+ "isFetching": null,
141
145
  "lastError": null,
142
146
  "lastErrorUpdate": null,
143
147
  "lastFetch": null,
@@ -175,6 +179,7 @@ Object {
175
179
  "fetchedPagesCount": 1,
176
180
  "hasMore": false,
177
181
  "id": "a",
182
+ "isFetching": null,
178
183
  "lastError": null,
179
184
  "lastErrorUpdate": null,
180
185
  "lastFetch": 1337,
@@ -209,6 +214,7 @@ Object {
209
214
  "fetchedPagesCount": 0,
210
215
  "hasMore": false,
211
216
  "id": "b",
217
+ "isFetching": null,
212
218
  "lastError": null,
213
219
  "lastErrorUpdate": null,
214
220
  "lastFetch": null,
@@ -252,6 +258,7 @@ Object {
252
258
  "fetchedPagesCount": 1,
253
259
  "hasMore": false,
254
260
  "id": "a",
261
+ "isFetching": null,
255
262
  "lastError": null,
256
263
  "lastErrorUpdate": null,
257
264
  "lastFetch": 1337,
@@ -284,6 +291,7 @@ Object {
284
291
  "fetchedPagesCount": 0,
285
292
  "hasMore": false,
286
293
  "id": "b",
294
+ "isFetching": null,
287
295
  "lastError": null,
288
296
  "lastErrorUpdate": null,
289
297
  "lastFetch": null,
@@ -322,6 +330,7 @@ Object {
322
330
  "fetchedPagesCount": 1,
323
331
  "hasMore": false,
324
332
  "id": "a",
333
+ "isFetching": null,
325
334
  "lastError": null,
326
335
  "lastErrorUpdate": null,
327
336
  "lastFetch": 1337,
@@ -355,6 +364,7 @@ Object {
355
364
  "fetchedPagesCount": 0,
356
365
  "hasMore": false,
357
366
  "id": "b",
367
+ "isFetching": null,
358
368
  "lastError": null,
359
369
  "lastErrorUpdate": null,
360
370
  "lastFetch": null,
@@ -392,6 +402,7 @@ Object {
392
402
  "fetchedPagesCount": 1,
393
403
  "hasMore": false,
394
404
  "id": "a",
405
+ "isFetching": null,
395
406
  "lastError": null,
396
407
  "lastErrorUpdate": null,
397
408
  "lastFetch": 1337,
@@ -424,6 +435,7 @@ Object {
424
435
  "fetchedPagesCount": 0,
425
436
  "hasMore": false,
426
437
  "id": "b",
438
+ "isFetching": null,
427
439
  "lastError": null,
428
440
  "lastErrorUpdate": null,
429
441
  "lastFetch": null,
@@ -463,6 +475,7 @@ Object {
463
475
  "fetchedPagesCount": 0,
464
476
  "hasMore": false,
465
477
  "id": "a",
478
+ "isFetching": null,
466
479
  "lastError": null,
467
480
  "lastErrorUpdate": null,
468
481
  "lastFetch": null,
@@ -493,6 +506,7 @@ Object {
493
506
  "fetchedPagesCount": 0,
494
507
  "hasMore": false,
495
508
  "id": "b",
509
+ "isFetching": null,
496
510
  "lastError": null,
497
511
  "lastErrorUpdate": null,
498
512
  "lastFetch": 1337,
@@ -530,6 +544,7 @@ Object {
530
544
  "fetchedPagesCount": 2,
531
545
  "hasMore": false,
532
546
  "id": "a",
547
+ "isFetching": null,
533
548
  "lastError": null,
534
549
  "lastErrorUpdate": null,
535
550
  "lastFetch": 1337,
@@ -564,6 +579,7 @@ Object {
564
579
  "fetchedPagesCount": 0,
565
580
  "hasMore": false,
566
581
  "id": "b",
582
+ "isFetching": null,
567
583
  "lastError": null,
568
584
  "lastErrorUpdate": null,
569
585
  "lastFetch": null,
@@ -605,6 +621,7 @@ Object {
605
621
  "fetchedPagesCount": 1,
606
622
  "hasMore": false,
607
623
  "id": "a",
624
+ "isFetching": null,
608
625
  "lastError": null,
609
626
  "lastErrorUpdate": null,
610
627
  "lastFetch": 1337,
@@ -637,6 +654,7 @@ Object {
637
654
  "fetchedPagesCount": 0,
638
655
  "hasMore": false,
639
656
  "id": "b",
657
+ "isFetching": null,
640
658
  "lastError": null,
641
659
  "lastErrorUpdate": null,
642
660
  "lastFetch": null,
@@ -84,6 +84,7 @@ var queryInitialState = {
84
84
  id: null,
85
85
  definition: null,
86
86
  fetchStatus: 'pending',
87
+ isFetching: null,
87
88
  lastFetch: null,
88
89
  lastUpdate: null,
89
90
  lastErrorUpdate: null,
@@ -187,10 +188,6 @@ var query = function query() {
187
188
  id: action.queryId,
188
189
  definition: action.queryDefinition,
189
190
  options: action.options,
190
- // When the query is new, we set "fetchStatus" to "loading"
191
- // directly since we know it will be loaded right away.
192
- // This way, the loadQuery action will have no effect, and
193
- // we save an additional render.
194
191
  fetchStatus: state.lastUpdate ? state.fetchStatus : 'pending'
195
192
  });
196
193
 
@@ -199,6 +196,13 @@ var query = function query() {
199
196
  return state;
200
197
  }
201
198
 
199
+ if (state.fetchStatus === 'loaded' && action.backgroundFetching) {
200
+ return _objectSpread(_objectSpread({}, state), {}, {
201
+ fetchStatus: 'loaded',
202
+ isFetching: true
203
+ });
204
+ }
205
+
202
206
  return _objectSpread(_objectSpread({}, state), {}, {
203
207
  fetchStatus: 'loading'
204
208
  });
@@ -212,6 +216,7 @@ var query = function query() {
212
216
  if (!response.data) {
213
217
  return _objectSpread(_objectSpread({}, state), {}, {
214
218
  fetchStatus: 'loaded',
219
+ isFetching: action.backgroundFetching ? false : null,
215
220
  lastFetch: Date.now(),
216
221
  lastUpdate: Date.now()
217
222
  });
@@ -221,6 +226,7 @@ var query = function query() {
221
226
 
222
227
  var common = _objectSpread({
223
228
  fetchStatus: 'loaded',
229
+ isFetching: action.backgroundFetching ? false : null,
224
230
  lastFetch: Date.now(),
225
231
  lastUpdate: Date.now()
226
232
  }, executionStatsEnabled && {
@@ -254,6 +260,7 @@ var query = function query() {
254
260
  return _objectSpread(_objectSpread({}, state), {}, {
255
261
  id: action.queryId,
256
262
  fetchStatus: 'failed',
263
+ isFetching: action.backgroundFetching ? false : null,
257
264
  lastError: action.error,
258
265
  lastErrorUpdate: Date.now()
259
266
  });
@@ -566,6 +573,7 @@ var _default = queries;
566
573
  * @param {string} queryId Name/id of the query
567
574
  * @param {QueryDefinition} queryDefinition - Definition of the created query
568
575
  * @param {QueryOptions} [options] - Options for the created query
576
+ * @returns {object} Redux action to dispatch
569
577
  */
570
578
 
571
579
  exports.default = _default;
@@ -584,15 +592,32 @@ var initQuery = function initQuery(queryId, queryDefinition) {
584
592
  options: options
585
593
  };
586
594
  };
595
+ /**
596
+ * Update the fetchStatus when the query is loading
597
+ *
598
+ * @param {string} queryId - id of the query
599
+ * @param {QueryOptions} [options] - Options for the created query
600
+ * @returns {object} Redux action to dispatch
601
+ */
602
+
587
603
 
588
604
  exports.initQuery = initQuery;
589
605
 
590
- var loadQuery = function loadQuery(queryId) {
591
- return {
606
+ var loadQuery = function loadQuery(queryId, options) {
607
+ return _objectSpread({
592
608
  type: LOAD_QUERY,
593
609
  queryId: queryId
594
- };
610
+ }, options);
595
611
  };
612
+ /**
613
+ * Update the fetchStatus when the query is loading
614
+ *
615
+ * @param {string} queryId - id of the query
616
+ * @param {object} response - The action response
617
+ * @param {QueryOptions} [options] - Options for the created query
618
+ * @returns {object} Redux action to dispatch
619
+ */
620
+
596
621
 
597
622
  exports.loadQuery = loadQuery;
598
623
 
@@ -604,15 +629,25 @@ var receiveQueryResult = function receiveQueryResult(queryId, response) {
604
629
  response: response
605
630
  }, options);
606
631
  };
632
+ /**
633
+ * Update the fetchStatus when the query is loading
634
+ *
635
+ * @param {string} queryId - id of the query
636
+ * @param {object} error - The action error
637
+ * @param {QueryOptions} [options] - Options for the created query
638
+ * @returns {object} Redux action to dispatch
639
+ */
640
+
607
641
 
608
642
  exports.receiveQueryResult = receiveQueryResult;
609
643
 
610
644
  var receiveQueryError = function receiveQueryError(queryId, error) {
611
- return {
645
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
646
+ return _objectSpread({
612
647
  type: RECEIVE_QUERY_ERROR,
613
648
  queryId: queryId,
614
649
  error: error
615
- };
650
+ }, options);
616
651
  }; // selectors
617
652
 
618
653
 
@@ -4,14 +4,18 @@ import queries, {
4
4
  convert$gtNullSelectors,
5
5
  makeSorterFromDefinition,
6
6
  mergeSelectorAndPartialIndex,
7
- sortAndLimitDocsIds
7
+ sortAndLimitDocsIds,
8
+ loadQuery,
9
+ receiveQueryError
8
10
  } from './queries'
9
11
  import { Q } from '../queries/dsl'
10
12
  import { TODO_1, TODO_2, TODO_3 } from '../__tests__/fixtures'
11
13
 
12
14
  describe('queries reducer', () => {
13
15
  let state
14
-
16
+ const response = {
17
+ data: [TODO_1, TODO_2]
18
+ }
15
19
  const applyAction = action => {
16
20
  state = queries(state, action)
17
21
  }
@@ -23,35 +27,116 @@ describe('queries reducer', () => {
23
27
  Date.now.mockRestore()
24
28
  })
25
29
 
26
- it('should init correctly', () => {
27
- const qdef = Q('io.cozy.todos')
28
- applyAction(initQuery('a', qdef))
29
- expect(state).toMatchSnapshot()
30
+ describe('INIT_QUERY', () => {
31
+ it('should init correctly', () => {
32
+ const qdef = Q('io.cozy.todos')
33
+ applyAction(initQuery('a', qdef))
34
+ expect(state).toMatchSnapshot()
35
+ })
36
+
37
+ test('init should have no effect after receiveQueryResult', () => {
38
+ const qdef = Q('io.cozy.todos')
39
+ applyAction(initQuery('a', qdef))
40
+ applyAction(receiveQueryResult('a', response))
41
+ let state1 = state
42
+ applyAction(initQuery('a', qdef))
43
+ expect(state1).toBe(state)
44
+ })
45
+
46
+ test('init should not set status to loading after receiveQueryResult', () => {
47
+ const qdef = Q('io.cozy.todos')
48
+ applyAction(initQuery('a', qdef))
49
+ applyAction(receiveQueryResult('a', response))
50
+ applyAction(initQuery('a', qdef))
51
+ expect(state.a.fetchStatus).toBe('loaded')
52
+ })
30
53
  })
31
54
 
32
- test('init should have no effect after receiveQueryResult', () => {
33
- const qdef = Q('io.cozy.todos')
34
- applyAction(initQuery('a', qdef))
35
- applyAction(
36
- receiveQueryResult('a', {
37
- data: [TODO_1, TODO_2]
38
- })
39
- )
40
- let state1 = state
41
- applyAction(initQuery('a', qdef))
42
- expect(state1).toBe(state)
55
+ describe('LOAD_QUERY', () => {
56
+ it('should set isFetching to true and not changing fetchStatus when query has been loaded and background fetching activated', () => {
57
+ // Given
58
+ const queryDefinition = Q('io.cozy.todos')
59
+ applyAction(initQuery('a', queryDefinition))
60
+ applyAction(receiveQueryResult('a', response))
61
+ expect(state.a.fetchStatus).toBe('loaded')
62
+
63
+ // When
64
+ applyAction(loadQuery('a', { backgroundFetching: true }))
65
+
66
+ // Then
67
+ expect(state.a.fetchStatus).toBe('loaded')
68
+ expect(state.a.isFetching).toBe(true)
69
+ })
70
+
71
+ it('should not set isFetching and keep changing fetchStatus when query has been loaded and background fetching deactivated', () => {
72
+ // Given
73
+ const queryDefinition = Q('io.cozy.todos')
74
+ applyAction(initQuery('a', queryDefinition))
75
+ applyAction(receiveQueryResult('a', response))
76
+ expect(state.a.fetchStatus).toBe('loaded')
77
+
78
+ // When
79
+ applyAction(loadQuery('a'))
80
+
81
+ // Then
82
+ expect(state.a.fetchStatus).toBe('loading')
83
+ expect(state.a.isFetching).toBe(null)
84
+ })
43
85
  })
44
86
 
45
- test('init should not set status to loading after receiveQueryResult', () => {
46
- const qdef = Q('io.cozy.todos')
47
- applyAction(initQuery('a', qdef))
48
- applyAction(
49
- receiveQueryResult('a', {
50
- data: [TODO_1, TODO_2]
51
- })
52
- )
53
- applyAction(initQuery('a', qdef))
54
- expect(state.a.fetchStatus).toBe('loaded')
87
+ describe('RECEIVE_QUERY_RESULT', () => {
88
+ it('should set isFetching to false when query has been re-fetched correctly and background fetching activated', () => {
89
+ // Given
90
+ const queryDefinition = Q('io.cozy.todos')
91
+ applyAction(initQuery('a', queryDefinition))
92
+
93
+ // When
94
+ applyAction(
95
+ receiveQueryResult('a', response, { backgroundFetching: true })
96
+ )
97
+
98
+ // Then
99
+ expect(state.a.isFetching).toBe(false)
100
+ })
101
+
102
+ it('should set isFetching to false when query has been re-fetched correctly and background fetching deactivated', () => {
103
+ // Given
104
+ const queryDefinition = Q('io.cozy.todos')
105
+ applyAction(initQuery('a', queryDefinition))
106
+
107
+ // When
108
+ applyAction(receiveQueryResult('a', response))
109
+
110
+ // Then
111
+ expect(state.a.isFetching).toBe(null)
112
+ })
113
+ })
114
+
115
+ describe('RECEIVE_QUERY_ERROR', () => {
116
+ it('should set isFetching to false when query is in error and background fetching activated', () => {
117
+ // Given
118
+ const queryDefinition = Q('io.cozy.todos')
119
+ applyAction(initQuery('a', queryDefinition))
120
+ const error = {}
121
+
122
+ // When
123
+ applyAction(receiveQueryError('a', error, { backgroundFetching: true }))
124
+
125
+ // Then
126
+ expect(state.a.isFetching).toBe(false)
127
+ })
128
+ it('should not set isFetching when query is in error and background fetching deactivated', () => {
129
+ // Given
130
+ const queryDefinition = Q('io.cozy.todos')
131
+ applyAction(initQuery('a', queryDefinition))
132
+ const error = {}
133
+
134
+ // When
135
+ applyAction(receiveQueryError('a', error))
136
+
137
+ // Then
138
+ expect(state.a.isFetching).toBe(null)
139
+ })
55
140
  })
56
141
 
57
142
  describe('updates', () => {
@@ -397,6 +397,7 @@ describe('Store', () => {
397
397
  definition: null,
398
398
  id: 'allTodos',
399
399
  fetchStatus: 'pending',
400
+ isFetching: null,
400
401
  lastFetch: null,
401
402
  lastUpdate: null,
402
403
  lastError: null,
package/dist/types.js CHANGED
@@ -61,6 +61,7 @@ var _dsl = require("./queries/dsl");
61
61
  * @property {string} id
62
62
  * @property {QueryDefinition} definition
63
63
  * @property {QueryFetchStatus} fetchStatus
64
+ * @property {boolean} isFetching
64
65
  * @property {number} lastFetch
65
66
  * @property {number} lastUpdate
66
67
  * @property {number} lastErrorUpdate
@@ -89,6 +90,7 @@ var _dsl = require("./queries/dsl");
89
90
  * @property {string} [update] - Does not seem to be used
90
91
  * @property {Function} [onError] - Callback when the query is errored
91
92
  * @property {boolean} [enabled=true] - If set to false, the query won't be executed
93
+ * @property {boolean} [backgroundFetching] - If set to true, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
92
94
  * @property {object} [hydrated=true] - Whether documents should be returned already hydrated
93
95
  * @property {object} [singleDocData] - If true, the "data" returned will be
94
96
  * a single doc instead of an array for single doc queries. Defaults to false for backward
@@ -386,5 +388,27 @@ var _dsl = require("./queries/dsl");
386
388
  * @property {string} [codeVerifier]
387
389
  * @property {string} [codeChallenge]
388
390
  */
391
+
392
+ /**
393
+ * Subdomain type for a Cozy. Can be flat or nested subdomains
394
+ *
395
+ * Example of 'flat' domain: https://claude-notes.somedomain.fr
396
+ * Example of 'nested' domain: https://notes.claude.somedomain.fr
397
+ *
398
+ * @typedef {'flat'|'nested'} SubdomainType
399
+ */
400
+
401
+ /**
402
+ * Represents the different parts of a deconstructed Cozy link
403
+ *
404
+ * @typedef {object} CozyLinkData
405
+ * @property {string} cozyBaseDomain - The Cozy's domain (i.e. 'mycozy.cloud')
406
+ * @property {string} cozyName - The Cozy's name (i.e. 'claude')
407
+ * @property {string} [hash] - The link's path (i.e. '#/folder/SOME_FOLDER_ID')
408
+ * @property {string} [pathname] - The link's path (i.e. '/public/')
409
+ * @property {string} protocol - The link's protocol (i.e. 'https')
410
+ * @property {string} [searchParams] - The link's searchParams (i.e. 'id=SOME_FOLDER_ID&sharecode=SOME_SHARECODE')
411
+ * @property {string} slug - The link's slug (i.e. 'drive' or 'notes)
412
+ */
389
413
  var _default = {};
390
414
  exports.default = _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-client",
3
- "version": "32.3.3",
3
+ "version": "32.5.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.node.js",
6
6
  "browser": "dist/index.js",
@@ -47,7 +47,7 @@
47
47
  "@testing-library/react-hooks": "3.7.0",
48
48
  "babel-plugin-search-and-replace": "1.1.1",
49
49
  "btoa": "1.2.1",
50
- "cozy-device-helper": "2.1.0",
50
+ "cozy-device-helper": "2.2.2",
51
51
  "cozy-flags": "2.8.6",
52
52
  "cozy-logger": "1.7.0",
53
53
  "cozy-ui": "48.0.0",
@@ -73,5 +73,5 @@
73
73
  "react-native-ios11-devicecheck": "https://github.com/cozy/react-native-devicecheck#app-attest-v0.1"
74
74
  },
75
75
  "sideEffects": false,
76
- "gitHead": "e8d0916a43759433221c24dc62c48ca1627b5f96"
76
+ "gitHead": "bedad965985b8452907251d06c2acc20e1adfddf"
77
77
  }
@@ -8,6 +8,10 @@ export type ClientOptions = {
8
8
  stackClient?: object;
9
9
  warningForCustomHandlers?: boolean;
10
10
  autoHydrate?: boolean;
11
+ /**
12
+ * - If set to true, backgroundFetching will be enabled by default on every query. Meaning that, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
13
+ */
14
+ backgroundFetching?: boolean;
11
15
  oauth?: object;
12
16
  onTokenRefresh?: Function;
13
17
  /**
@@ -41,6 +45,7 @@ export type ClientOptions = {
41
45
  * @property {object} [stackClient]
42
46
  * @property {boolean} [warningForCustomHandlers]
43
47
  * @property {boolean} [autoHydrate]
48
+ * @property {boolean} [backgroundFetching] - If set to true, backgroundFetching will be enabled by default on every query. Meaning that, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
44
49
  * @property {object} [oauth]
45
50
  * @property {Function} [onTokenRefresh]
46
51
  * @property {Function} [onError] - Default callback if a query is errored
@@ -148,6 +153,10 @@ declare class CozyClient {
148
153
  stackClient?: object;
149
154
  warningForCustomHandlers?: boolean;
150
155
  autoHydrate?: boolean;
156
+ /**
157
+ * - If set to true, backgroundFetching will be enabled by default on every query. Meaning that, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
158
+ */
159
+ backgroundFetching?: boolean;
151
160
  oauth?: object;
152
161
  onTokenRefresh?: Function;
153
162
  /**
@@ -7,6 +7,7 @@ export function generateWebLink({ cozyUrl, searchParams: searchParamsOption, pat
7
7
  slug: string;
8
8
  subDomainType: string;
9
9
  }): string;
10
+ export function deconstructCozyWebLinkWithSlug(webLink: string, subDomainType?: SubdomainType): CozyLinkData;
10
11
  export class InvalidProtocolError extends Error {
11
12
  constructor(url: any);
12
13
  url: any;
@@ -16,3 +17,5 @@ export class InvalidCozyUrlError extends Error {
16
17
  url: any;
17
18
  }
18
19
  export function rootCozyUrl(url: URL): Promise<URL>;
20
+ import { SubdomainType } from "./types";
21
+ import { CozyLinkData } from "./types";
package/types/index.d.ts CHANGED
@@ -19,6 +19,6 @@ export { manifest, models };
19
19
  export { QueryDefinition, Q, Mutations, MutationTypes, getDoctypeFromOperation } from "./queries/dsl";
20
20
  export { Association, HasMany, HasOne, HasOneInPlace, HasManyInPlace, HasManyTriggers } from "./associations";
21
21
  export { isReferencedBy, isReferencedById, getReferencedBy, getReferencedById } from "./associations/helpers";
22
- export { dehydrate, generateWebLink, rootCozyUrl, InvalidCozyUrlError, InvalidProtocolError } from "./helpers";
22
+ export { deconstructCozyWebLinkWithSlug, dehydrate, generateWebLink, rootCozyUrl, InvalidCozyUrlError, InvalidProtocolError } from "./helpers";
23
23
  export { cancelable, isQueryLoading, hasQueryBeenLoaded } from "./utils";
24
24
  export { queryConnect, queryConnectFlat, withClient } from "./hoc";
@@ -8,26 +8,10 @@ export function convert$gtNullSelectors(selector: any): object;
8
8
  export function mergeSelectorAndPartialIndex(queryDefinition: object): object;
9
9
  export function makeSorterFromDefinition(definition: QueryDefinition): (arg0: Array<CozyClientDocument>) => Array<CozyClientDocument>;
10
10
  export default queries;
11
- export function initQuery(queryId: string, queryDefinition: QueryDefinition, options?: QueryOptions): {
12
- type: string;
13
- queryId: string;
14
- queryDefinition: QueryDefinition;
15
- options: QueryOptions;
16
- };
17
- export function loadQuery(queryId: any): {
18
- type: string;
19
- queryId: any;
20
- };
21
- export function receiveQueryResult(queryId: any, response: any, options?: {}): {
22
- type: string;
23
- queryId: any;
24
- response: any;
25
- };
26
- export function receiveQueryError(queryId: any, error: any): {
27
- type: string;
28
- queryId: any;
29
- error: any;
30
- };
11
+ export function initQuery(queryId: string, queryDefinition: QueryDefinition, options?: QueryOptions): object;
12
+ export function loadQuery(queryId: string, options?: QueryOptions): object;
13
+ export function receiveQueryResult(queryId: string, response: object, options?: QueryOptions): object;
14
+ export function receiveQueryError(queryId: string, error: object, options?: QueryOptions): object;
31
15
  export function getQueryFromSlice(state: any, queryId: any, documents: any): any;
32
16
  export class QueryIDGenerator {
33
17
  idCounter: number;
package/types/types.d.ts CHANGED
@@ -35,6 +35,7 @@ export type QueryState = {
35
35
  id: string;
36
36
  definition: QueryDefinition;
37
37
  fetchStatus: QueryFetchStatus;
38
+ isFetching: boolean;
38
39
  lastFetch: number;
39
40
  lastUpdate: number;
40
41
  lastErrorUpdate: number;
@@ -73,6 +74,10 @@ export type QueryOptions = {
73
74
  * - If set to false, the query won't be executed
74
75
  */
75
76
  enabled?: boolean;
77
+ /**
78
+ * - If set to true, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started.
79
+ */
80
+ backgroundFetching?: boolean;
76
81
  /**
77
82
  * - Whether documents should be returned already hydrated
78
83
  */
@@ -477,4 +482,44 @@ export type PKCECodes = {
477
482
  codeVerifier?: string;
478
483
  codeChallenge?: string;
479
484
  };
485
+ /**
486
+ * Subdomain type for a Cozy. Can be flat or nested subdomains
487
+ *
488
+ * Example of 'flat' domain: https://claude-notes.somedomain.fr
489
+ * Example of 'nested' domain: https://notes.claude.somedomain.fr
490
+ */
491
+ export type SubdomainType = "flat" | "nested";
492
+ /**
493
+ * Represents the different parts of a deconstructed Cozy link
494
+ */
495
+ export type CozyLinkData = {
496
+ /**
497
+ * - The Cozy's domain (i.e. 'mycozy.cloud')
498
+ */
499
+ cozyBaseDomain: string;
500
+ /**
501
+ * - The Cozy's name (i.e. 'claude')
502
+ */
503
+ cozyName: string;
504
+ /**
505
+ * - The link's path (i.e. '#/folder/SOME_FOLDER_ID')
506
+ */
507
+ hash?: string;
508
+ /**
509
+ * - The link's path (i.e. '/public/')
510
+ */
511
+ pathname?: string;
512
+ /**
513
+ * - The link's protocol (i.e. 'https')
514
+ */
515
+ protocol: string;
516
+ /**
517
+ * - The link's searchParams (i.e. 'id=SOME_FOLDER_ID&sharecode=SOME_SHARECODE')
518
+ */
519
+ searchParams?: string;
520
+ /**
521
+ * - The link's slug (i.e. 'drive' or 'notes)
522
+ */
523
+ slug: string;
524
+ };
480
525
  import { QueryDefinition } from "./queries/dsl";