@ethlete/query 2.0.0 → 2.1.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.
@@ -1,5 +1,5 @@
1
1
  import { BehaviorSubjectWithSubscriberCount, getCookie, setCookie, deleteCookie, DestroyService, equal, clone } from '@ethlete/core';
2
- import { Observable, filter, takeWhile, switchMap, of, BehaviorSubject, Subject, takeUntil, interval, tap, timer, fromEvent, startWith, concat, take, debounceTime, combineLatest, map, distinctUntilChanged } from 'rxjs';
2
+ import { Observable, filter, takeWhile, switchMap, of, BehaviorSubject, Subject, takeUntil, interval, startWith, tap, timer, fromEvent, concat, take, debounceTime, combineLatest, map, distinctUntilChanged } from 'rxjs';
3
3
  import * as i0 from '@angular/core';
4
4
  import { Directive, Input, InjectionToken, inject, ChangeDetectorRef, ViewContainerRef, TemplateRef, ErrorHandler, ElementRef } from '@angular/core';
5
5
  import { FormGroup } from '@angular/forms';
@@ -548,8 +548,22 @@ class Query {
548
548
  get isInUse() {
549
549
  return this._state$.subscriberCount > 0;
550
550
  }
551
- get _config() {
552
- return this._queryConfig;
551
+ get isPolling() {
552
+ return !!this._pollingSubscription;
553
+ }
554
+ get autoRefreshOnConfig() {
555
+ const base = this._queryConfig.autoRefreshOn ?? {};
556
+ const transformed = {
557
+ queryClientDefaultHeadersChange: base.queryClientDefaultHeadersChange ?? true,
558
+ windowFocus: base.windowFocus ?? true,
559
+ };
560
+ return transformed;
561
+ }
562
+ /**
563
+ * @internal
564
+ */
565
+ get _enableSmartPolling() {
566
+ return this._queryConfig.enableSmartPolling ?? true;
553
567
  }
554
568
  constructor(_client, _queryConfig, _routeWithParams, _args) {
555
569
  this._client = _client;
@@ -559,15 +573,21 @@ class Query {
559
573
  this._currentId = 0;
560
574
  this._pollingSubscription = null;
561
575
  this._onAbort$ = new Subject();
576
+ this._currentPollConfig = null;
577
+ /**
578
+ * @internal
579
+ */
580
+ this._isPollingPaused = false;
562
581
  this._state$ = new BehaviorSubjectWithSubscriberCount({
563
582
  type: "PREPARED" /* QueryStateType.Prepared */,
564
- meta: { id: this._currentId },
583
+ meta: { id: this._currentId, triggeredVia: 'program' },
565
584
  });
566
585
  }
567
586
  clone() {
568
587
  return this._client.fetch(this._queryConfig);
569
588
  }
570
589
  execute(options) {
590
+ const triggeredVia = options?._triggeredVia ?? 'program';
571
591
  if (!this.isExpired && !options?.skipCache && isQueryStateSuccess(this.state)) {
572
592
  return this;
573
593
  }
@@ -578,7 +598,7 @@ class Query {
578
598
  if (isQueryStateLoading(this._state$.value)) {
579
599
  this.abort();
580
600
  }
581
- const meta = { id };
601
+ const meta = { id, triggeredVia };
582
602
  this._state$.next({
583
603
  type: "LOADING" /* QueryStateType.Loading */,
584
604
  meta,
@@ -688,16 +708,34 @@ class Query {
688
708
  if (this._pollingSubscription) {
689
709
  return this;
690
710
  }
691
- this._pollingSubscription = interval(config.interval)
711
+ this._currentPollConfig = config;
712
+ const _interval = config.triggerImmediately
713
+ ? interval(config.interval).pipe(startWith(-1))
714
+ : interval(config.interval);
715
+ this._pollingSubscription = _interval
692
716
  .pipe(takeUntil(config.takeUntil))
693
- .subscribe(() => this.execute({ skipCache: true }));
717
+ .subscribe(() => this.execute({ skipCache: true, _triggeredVia: 'poll' }));
694
718
  return this;
695
719
  }
696
720
  stopPolling() {
697
721
  this._pollingSubscription?.unsubscribe();
698
722
  this._pollingSubscription = null;
723
+ this._currentPollConfig = null;
724
+ return this;
725
+ }
726
+ pausePolling() {
727
+ this._pollingSubscription?.unsubscribe();
728
+ this._pollingSubscription = null;
729
+ this._isPollingPaused = true;
699
730
  return this;
700
731
  }
732
+ resumePolling() {
733
+ if (!this._isPollingPaused || !this._currentPollConfig) {
734
+ return this;
735
+ }
736
+ this._isPollingPaused = false;
737
+ return this.poll({ ...this._currentPollConfig, triggerImmediately: true });
738
+ }
701
739
  _updateUseResultInDependencies() {
702
740
  if (!this._args?.useResultIn?.length) {
703
741
  return;
@@ -963,9 +1001,9 @@ class QueryDirective {
963
1001
  }
964
1002
  }
965
1003
  }
966
- QueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: QueryDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: i0.ErrorHandler }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
967
- QueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.4", type: QueryDirective, isStandalone: true, selector: "[query]", inputs: { query: "query", cache: ["queryCache", "cache"] }, ngImport: i0 });
968
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: QueryDirective, decorators: [{
1004
+ QueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: QueryDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: i0.ErrorHandler }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
1005
+ QueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.1", type: QueryDirective, isStandalone: true, selector: "[query]", inputs: { query: "query", cache: ["queryCache", "cache"] }, ngImport: i0 });
1006
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: QueryDirective, decorators: [{
969
1007
  type: Directive,
970
1008
  args: [{
971
1009
  // eslint-disable-next-line @angular-eslint/directive-selector
@@ -1038,6 +1076,7 @@ class InfinityQuery {
1038
1076
  console.error('Cannot load more pages, already at the end. Make sure to not render the infinity query trigger using *ngIf canLoadMore');
1039
1077
  return;
1040
1078
  }
1079
+ this._clearSubscriptions();
1041
1080
  const args = this._prepareArgs(this._config, calculatedPage);
1042
1081
  const query = this._config.queryCreator.prepare(args).execute();
1043
1082
  this._handleNewQuery(query);
@@ -1046,7 +1085,7 @@ class InfinityQuery {
1046
1085
  this._currentQuery$.next(query);
1047
1086
  }
1048
1087
  reset(newConfig) {
1049
- this._destroy();
1088
+ this._clearSubscriptions();
1050
1089
  this._config = { ...this._config, ...(newConfig ?? {}) };
1051
1090
  this._currentPage$.next(null);
1052
1091
  this._currentCalculatedPage$.next(null);
@@ -1054,8 +1093,9 @@ class InfinityQuery {
1054
1093
  this._itemsPerPage$.next(this._config.limitParam?.value ?? 10);
1055
1094
  this._currentQuery$.next(null);
1056
1095
  this._data$.next([]);
1096
+ this.nextPage();
1057
1097
  }
1058
- _destroy() {
1098
+ _clearSubscriptions() {
1059
1099
  this._subscriptions.forEach((sub) => sub.unsubscribe());
1060
1100
  this._subscriptions = [];
1061
1101
  }
@@ -1085,7 +1125,14 @@ class InfinityQuery {
1085
1125
  this._subscriptions = this._subscriptions.filter((sub) => sub !== stateChangesSub);
1086
1126
  },
1087
1127
  });
1088
- this._subscriptions.push(stateChangesSub);
1128
+ const queryAutoRefreshSub = query.state$.subscribe({
1129
+ next: (state) => {
1130
+ if (state.meta.triggeredVia === 'auto') {
1131
+ this.reset();
1132
+ }
1133
+ },
1134
+ });
1135
+ this._subscriptions.push(stateChangesSub, queryAutoRefreshSub);
1089
1136
  }
1090
1137
  _prepareArgs(config, page) {
1091
1138
  const pageParamLocation = this._getPageParamLocation(config?.pageParam?.location);
@@ -1199,7 +1246,7 @@ class InfinityQueryDirective {
1199
1246
  this._renderMainView();
1200
1247
  }
1201
1248
  ngOnDestroy() {
1202
- this.instance?._destroy();
1249
+ this.instance?._clearSubscriptions();
1203
1250
  }
1204
1251
  _setupInfinityQuery(config) {
1205
1252
  const instance = new InfinityQuery(config);
@@ -1265,7 +1312,7 @@ class InfinityQueryDirective {
1265
1312
  }
1266
1313
  _cleanup() {
1267
1314
  this._queryConfigChanged$.next(true);
1268
- this.instance?._destroy();
1315
+ this.instance?._clearSubscriptions();
1269
1316
  this._viewContext.loading = false;
1270
1317
  this._viewContext.error = null;
1271
1318
  this._viewContext.infinityQuery = null;
@@ -1278,9 +1325,9 @@ class InfinityQueryDirective {
1278
1325
  this._data$.next([]);
1279
1326
  }
1280
1327
  }
1281
- InfinityQueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: InfinityQueryDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1282
- InfinityQueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.4", type: InfinityQueryDirective, isStandalone: true, selector: "[infinityQuery]", inputs: { infinityQuery: "infinityQuery" }, providers: [{ provide: INFINITY_QUERY_TOKEN, useExisting: InfinityQueryDirective }, DestroyService], exportAs: ["infinityQuery"], ngImport: i0 });
1283
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: InfinityQueryDirective, decorators: [{
1328
+ InfinityQueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: InfinityQueryDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1329
+ InfinityQueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.1", type: InfinityQueryDirective, isStandalone: true, selector: "[infinityQuery]", inputs: { infinityQuery: "infinityQuery" }, providers: [{ provide: INFINITY_QUERY_TOKEN, useExisting: InfinityQueryDirective }, DestroyService], exportAs: ["infinityQuery"], ngImport: i0 });
1330
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: InfinityQueryDirective, decorators: [{
1284
1331
  type: Directive,
1285
1332
  args: [{
1286
1333
  // eslint-disable-next-line @angular-eslint/directive-selector
@@ -1330,9 +1377,9 @@ class InfinityQueryTriggerDirective {
1330
1377
  this._observer.observe(this._elementRef.nativeElement);
1331
1378
  }
1332
1379
  }
1333
- InfinityQueryTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: InfinityQueryTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1334
- InfinityQueryTriggerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.4", type: InfinityQueryTriggerDirective, isStandalone: true, selector: "[infinityQueryTrigger], infinity-query-trigger", inputs: { scrollContainerSelector: "scrollContainerSelector" }, ngImport: i0 });
1335
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: InfinityQueryTriggerDirective, decorators: [{
1380
+ InfinityQueryTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: InfinityQueryTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1381
+ InfinityQueryTriggerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.1", type: InfinityQueryTriggerDirective, isStandalone: true, selector: "[infinityQueryTrigger], infinity-query-trigger", inputs: { scrollContainerSelector: "scrollContainerSelector" }, ngImport: i0 });
1382
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: InfinityQueryTriggerDirective, decorators: [{
1336
1383
  type: Directive,
1337
1384
  args: [{
1338
1385
  // eslint-disable-next-line @angular-eslint/directive-selector
@@ -1348,6 +1395,7 @@ class QueryStore {
1348
1395
  this._config = _config;
1349
1396
  this._store = new Map();
1350
1397
  this._garbageCollector = null;
1398
+ this._initSmartQueryHandling();
1351
1399
  }
1352
1400
  add(id, query) {
1353
1401
  this._store.set(id, query);
@@ -1371,14 +1419,45 @@ class QueryStore {
1371
1419
  for (const [key, query] of this._store) {
1372
1420
  if (query.isInUse &&
1373
1421
  (query.isExpired || ignoreCacheValidity) &&
1374
- (query._config.autoRefreshOn?.queryClientDefaultHeadersChange ?? true)) {
1375
- query.execute({ skipCache: true });
1422
+ query.autoRefreshOnConfig.queryClientDefaultHeadersChange) {
1423
+ query.execute({ skipCache: true, _triggeredVia: 'auto' });
1376
1424
  }
1377
1425
  else if (purgeUnused && !query.isInUse) {
1378
1426
  this.remove(key);
1379
1427
  }
1380
1428
  }
1381
1429
  }
1430
+ _initSmartQueryHandling() {
1431
+ const windowBlur$ = fromEvent(window, 'blur');
1432
+ const windowFocus$ = fromEvent(window, 'focus');
1433
+ windowBlur$.subscribe(() => {
1434
+ this._stopGarbageCollector();
1435
+ if (this._config?.enableSmartPolling) {
1436
+ this.forEach((query) => {
1437
+ if (!query.isPolling || !query._enableSmartPolling) {
1438
+ return;
1439
+ }
1440
+ query.pausePolling();
1441
+ });
1442
+ }
1443
+ });
1444
+ windowFocus$.subscribe(() => {
1445
+ if (this._config?.enableSmartPolling || this._config?.autoRefreshQueriesOnWindowFocus) {
1446
+ this.forEach((query) => {
1447
+ if (this._config?.enableSmartPolling && query._isPollingPaused) {
1448
+ query.resumePolling();
1449
+ }
1450
+ if (this._config?.autoRefreshQueriesOnWindowFocus &&
1451
+ query.isExpired &&
1452
+ query.isInUse &&
1453
+ query.autoRefreshOnConfig.windowFocus) {
1454
+ query.execute({ skipCache: true, _triggeredVia: 'auto' });
1455
+ }
1456
+ });
1457
+ }
1458
+ this._initGarbageCollector();
1459
+ });
1460
+ }
1382
1461
  _logState(key, item, operation) {
1383
1462
  if (!this._config?.enableChangeLogging)
1384
1463
  return;
@@ -1538,6 +1617,8 @@ class QueryClient {
1538
1617
  this._store = new QueryStore({
1539
1618
  enableChangeLogging: _clientConfig.logging?.queryStateChanges,
1540
1619
  enableGarbageCollectorLogging: _clientConfig.logging?.queryStateGarbageCollector,
1620
+ autoRefreshQueriesOnWindowFocus: _clientConfig.request?.autoRefreshQueriesOnWindowFocus ?? true,
1621
+ enableSmartPolling: _clientConfig.request?.enableSmartPolling ?? true,
1541
1622
  });
1542
1623
  }
1543
1624
  }