@ethlete/query 2.0.0 → 2.1.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.
- package/esm2020/lib/directives/infinity-query-trigger.directive.mjs +3 -3
- package/esm2020/lib/directives/infinity-query.directive.mjs +6 -6
- package/esm2020/lib/directives/query.directive.mjs +3 -3
- package/esm2020/lib/infinite-query/infinity-query.mjs +13 -4
- package/esm2020/lib/query/query.mjs +46 -8
- package/esm2020/lib/query/query.types.mjs +1 -1
- package/esm2020/lib/query-client/query-client.mjs +3 -1
- package/esm2020/lib/query-client/query-client.types.mjs +1 -1
- package/esm2020/lib/query-store/query-store.mjs +36 -3
- package/esm2020/lib/request/request.util.mjs +4 -1
- package/fesm2015/ethlete-query.mjs +121 -32
- package/fesm2015/ethlete-query.mjs.map +1 -1
- package/fesm2020/ethlete-query.mjs +107 -23
- package/fesm2020/ethlete-query.mjs.map +1 -1
- package/lib/auth/auth-provider.types.d.ts +0 -5
- package/lib/infinite-query/infinity-query.d.ts +1 -1
- package/lib/query/query.d.ts +6 -2
- package/lib/query/query.types.d.ts +39 -7
- package/lib/query-client/query-client.types.d.ts +11 -0
- package/lib/query-store/query-store.d.ts +3 -0
- package/package.json +2 -2
|
@@ -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,
|
|
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';
|
|
@@ -163,6 +163,9 @@ const extractExpiresInSeconds = (headers) => {
|
|
|
163
163
|
const ageSeconds = parseInt(age);
|
|
164
164
|
expiresIn = maxAge - ageSeconds;
|
|
165
165
|
}
|
|
166
|
+
else if (maxAge) {
|
|
167
|
+
expiresIn = maxAge / 2; // We assume the response is half way to its expiration
|
|
168
|
+
}
|
|
166
169
|
else if (expires) {
|
|
167
170
|
// Used by some apis to tell the response will never expire
|
|
168
171
|
// In this case we let the response expire after 1 hour
|
|
@@ -548,8 +551,22 @@ class Query {
|
|
|
548
551
|
get isInUse() {
|
|
549
552
|
return this._state$.subscriberCount > 0;
|
|
550
553
|
}
|
|
551
|
-
get
|
|
552
|
-
return this.
|
|
554
|
+
get isPolling() {
|
|
555
|
+
return !!this._pollingSubscription;
|
|
556
|
+
}
|
|
557
|
+
get autoRefreshOnConfig() {
|
|
558
|
+
const base = this._queryConfig.autoRefreshOn ?? {};
|
|
559
|
+
const transformed = {
|
|
560
|
+
queryClientDefaultHeadersChange: base.queryClientDefaultHeadersChange ?? true,
|
|
561
|
+
windowFocus: base.windowFocus ?? true,
|
|
562
|
+
};
|
|
563
|
+
return transformed;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* @internal
|
|
567
|
+
*/
|
|
568
|
+
get _enableSmartPolling() {
|
|
569
|
+
return this._queryConfig.enableSmartPolling ?? true;
|
|
553
570
|
}
|
|
554
571
|
constructor(_client, _queryConfig, _routeWithParams, _args) {
|
|
555
572
|
this._client = _client;
|
|
@@ -559,15 +576,21 @@ class Query {
|
|
|
559
576
|
this._currentId = 0;
|
|
560
577
|
this._pollingSubscription = null;
|
|
561
578
|
this._onAbort$ = new Subject();
|
|
579
|
+
this._currentPollConfig = null;
|
|
580
|
+
/**
|
|
581
|
+
* @internal
|
|
582
|
+
*/
|
|
583
|
+
this._isPollingPaused = false;
|
|
562
584
|
this._state$ = new BehaviorSubjectWithSubscriberCount({
|
|
563
585
|
type: "PREPARED" /* QueryStateType.Prepared */,
|
|
564
|
-
meta: { id: this._currentId },
|
|
586
|
+
meta: { id: this._currentId, triggeredVia: 'program' },
|
|
565
587
|
});
|
|
566
588
|
}
|
|
567
589
|
clone() {
|
|
568
590
|
return this._client.fetch(this._queryConfig);
|
|
569
591
|
}
|
|
570
592
|
execute(options) {
|
|
593
|
+
const triggeredVia = options?._triggeredVia ?? 'program';
|
|
571
594
|
if (!this.isExpired && !options?.skipCache && isQueryStateSuccess(this.state)) {
|
|
572
595
|
return this;
|
|
573
596
|
}
|
|
@@ -578,7 +601,7 @@ class Query {
|
|
|
578
601
|
if (isQueryStateLoading(this._state$.value)) {
|
|
579
602
|
this.abort();
|
|
580
603
|
}
|
|
581
|
-
const meta = { id };
|
|
604
|
+
const meta = { id, triggeredVia };
|
|
582
605
|
this._state$.next({
|
|
583
606
|
type: "LOADING" /* QueryStateType.Loading */,
|
|
584
607
|
meta,
|
|
@@ -688,16 +711,34 @@ class Query {
|
|
|
688
711
|
if (this._pollingSubscription) {
|
|
689
712
|
return this;
|
|
690
713
|
}
|
|
691
|
-
this.
|
|
714
|
+
this._currentPollConfig = config;
|
|
715
|
+
const _interval = config.triggerImmediately
|
|
716
|
+
? interval(config.interval).pipe(startWith(-1))
|
|
717
|
+
: interval(config.interval);
|
|
718
|
+
this._pollingSubscription = _interval
|
|
692
719
|
.pipe(takeUntil(config.takeUntil))
|
|
693
|
-
.subscribe(() => this.execute({ skipCache: true }));
|
|
720
|
+
.subscribe(() => this.execute({ skipCache: true, _triggeredVia: 'poll' }));
|
|
694
721
|
return this;
|
|
695
722
|
}
|
|
696
723
|
stopPolling() {
|
|
697
724
|
this._pollingSubscription?.unsubscribe();
|
|
698
725
|
this._pollingSubscription = null;
|
|
726
|
+
this._currentPollConfig = null;
|
|
699
727
|
return this;
|
|
700
728
|
}
|
|
729
|
+
pausePolling() {
|
|
730
|
+
this._pollingSubscription?.unsubscribe();
|
|
731
|
+
this._pollingSubscription = null;
|
|
732
|
+
this._isPollingPaused = true;
|
|
733
|
+
return this;
|
|
734
|
+
}
|
|
735
|
+
resumePolling() {
|
|
736
|
+
if (!this._isPollingPaused || !this._currentPollConfig) {
|
|
737
|
+
return this;
|
|
738
|
+
}
|
|
739
|
+
this._isPollingPaused = false;
|
|
740
|
+
return this.poll({ ...this._currentPollConfig, triggerImmediately: true });
|
|
741
|
+
}
|
|
701
742
|
_updateUseResultInDependencies() {
|
|
702
743
|
if (!this._args?.useResultIn?.length) {
|
|
703
744
|
return;
|
|
@@ -963,9 +1004,9 @@ class QueryDirective {
|
|
|
963
1004
|
}
|
|
964
1005
|
}
|
|
965
1006
|
}
|
|
966
|
-
QueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1
|
|
967
|
-
QueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1
|
|
968
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1
|
|
1007
|
+
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 });
|
|
1008
|
+
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 });
|
|
1009
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: QueryDirective, decorators: [{
|
|
969
1010
|
type: Directive,
|
|
970
1011
|
args: [{
|
|
971
1012
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
@@ -1038,6 +1079,7 @@ class InfinityQuery {
|
|
|
1038
1079
|
console.error('Cannot load more pages, already at the end. Make sure to not render the infinity query trigger using *ngIf canLoadMore');
|
|
1039
1080
|
return;
|
|
1040
1081
|
}
|
|
1082
|
+
this._clearSubscriptions();
|
|
1041
1083
|
const args = this._prepareArgs(this._config, calculatedPage);
|
|
1042
1084
|
const query = this._config.queryCreator.prepare(args).execute();
|
|
1043
1085
|
this._handleNewQuery(query);
|
|
@@ -1046,7 +1088,7 @@ class InfinityQuery {
|
|
|
1046
1088
|
this._currentQuery$.next(query);
|
|
1047
1089
|
}
|
|
1048
1090
|
reset(newConfig) {
|
|
1049
|
-
this.
|
|
1091
|
+
this._clearSubscriptions();
|
|
1050
1092
|
this._config = { ...this._config, ...(newConfig ?? {}) };
|
|
1051
1093
|
this._currentPage$.next(null);
|
|
1052
1094
|
this._currentCalculatedPage$.next(null);
|
|
@@ -1054,8 +1096,9 @@ class InfinityQuery {
|
|
|
1054
1096
|
this._itemsPerPage$.next(this._config.limitParam?.value ?? 10);
|
|
1055
1097
|
this._currentQuery$.next(null);
|
|
1056
1098
|
this._data$.next([]);
|
|
1099
|
+
this.nextPage();
|
|
1057
1100
|
}
|
|
1058
|
-
|
|
1101
|
+
_clearSubscriptions() {
|
|
1059
1102
|
this._subscriptions.forEach((sub) => sub.unsubscribe());
|
|
1060
1103
|
this._subscriptions = [];
|
|
1061
1104
|
}
|
|
@@ -1085,7 +1128,14 @@ class InfinityQuery {
|
|
|
1085
1128
|
this._subscriptions = this._subscriptions.filter((sub) => sub !== stateChangesSub);
|
|
1086
1129
|
},
|
|
1087
1130
|
});
|
|
1088
|
-
|
|
1131
|
+
const queryAutoRefreshSub = query.state$.subscribe({
|
|
1132
|
+
next: (state) => {
|
|
1133
|
+
if (state.meta.triggeredVia === 'auto') {
|
|
1134
|
+
this.reset();
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
});
|
|
1138
|
+
this._subscriptions.push(stateChangesSub, queryAutoRefreshSub);
|
|
1089
1139
|
}
|
|
1090
1140
|
_prepareArgs(config, page) {
|
|
1091
1141
|
const pageParamLocation = this._getPageParamLocation(config?.pageParam?.location);
|
|
@@ -1199,7 +1249,7 @@ class InfinityQueryDirective {
|
|
|
1199
1249
|
this._renderMainView();
|
|
1200
1250
|
}
|
|
1201
1251
|
ngOnDestroy() {
|
|
1202
|
-
this.instance?.
|
|
1252
|
+
this.instance?._clearSubscriptions();
|
|
1203
1253
|
}
|
|
1204
1254
|
_setupInfinityQuery(config) {
|
|
1205
1255
|
const instance = new InfinityQuery(config);
|
|
@@ -1265,7 +1315,7 @@ class InfinityQueryDirective {
|
|
|
1265
1315
|
}
|
|
1266
1316
|
_cleanup() {
|
|
1267
1317
|
this._queryConfigChanged$.next(true);
|
|
1268
|
-
this.instance?.
|
|
1318
|
+
this.instance?._clearSubscriptions();
|
|
1269
1319
|
this._viewContext.loading = false;
|
|
1270
1320
|
this._viewContext.error = null;
|
|
1271
1321
|
this._viewContext.infinityQuery = null;
|
|
@@ -1278,9 +1328,9 @@ class InfinityQueryDirective {
|
|
|
1278
1328
|
this._data$.next([]);
|
|
1279
1329
|
}
|
|
1280
1330
|
}
|
|
1281
|
-
InfinityQueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1
|
|
1282
|
-
InfinityQueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1
|
|
1283
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1
|
|
1331
|
+
InfinityQueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: InfinityQueryDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1332
|
+
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 });
|
|
1333
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: InfinityQueryDirective, decorators: [{
|
|
1284
1334
|
type: Directive,
|
|
1285
1335
|
args: [{
|
|
1286
1336
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
@@ -1330,9 +1380,9 @@ class InfinityQueryTriggerDirective {
|
|
|
1330
1380
|
this._observer.observe(this._elementRef.nativeElement);
|
|
1331
1381
|
}
|
|
1332
1382
|
}
|
|
1333
|
-
InfinityQueryTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1
|
|
1334
|
-
InfinityQueryTriggerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1
|
|
1335
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1
|
|
1383
|
+
InfinityQueryTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: InfinityQueryTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1384
|
+
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 });
|
|
1385
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: InfinityQueryTriggerDirective, decorators: [{
|
|
1336
1386
|
type: Directive,
|
|
1337
1387
|
args: [{
|
|
1338
1388
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
@@ -1348,6 +1398,7 @@ class QueryStore {
|
|
|
1348
1398
|
this._config = _config;
|
|
1349
1399
|
this._store = new Map();
|
|
1350
1400
|
this._garbageCollector = null;
|
|
1401
|
+
this._initSmartQueryHandling();
|
|
1351
1402
|
}
|
|
1352
1403
|
add(id, query) {
|
|
1353
1404
|
this._store.set(id, query);
|
|
@@ -1371,14 +1422,45 @@ class QueryStore {
|
|
|
1371
1422
|
for (const [key, query] of this._store) {
|
|
1372
1423
|
if (query.isInUse &&
|
|
1373
1424
|
(query.isExpired || ignoreCacheValidity) &&
|
|
1374
|
-
|
|
1375
|
-
query.execute({ skipCache: true });
|
|
1425
|
+
query.autoRefreshOnConfig.queryClientDefaultHeadersChange) {
|
|
1426
|
+
query.execute({ skipCache: true, _triggeredVia: 'auto' });
|
|
1376
1427
|
}
|
|
1377
1428
|
else if (purgeUnused && !query.isInUse) {
|
|
1378
1429
|
this.remove(key);
|
|
1379
1430
|
}
|
|
1380
1431
|
}
|
|
1381
1432
|
}
|
|
1433
|
+
_initSmartQueryHandling() {
|
|
1434
|
+
const windowBlur$ = fromEvent(window, 'blur');
|
|
1435
|
+
const windowFocus$ = fromEvent(window, 'focus');
|
|
1436
|
+
windowBlur$.subscribe(() => {
|
|
1437
|
+
this._stopGarbageCollector();
|
|
1438
|
+
if (this._config?.enableSmartPolling) {
|
|
1439
|
+
this.forEach((query) => {
|
|
1440
|
+
if (!query.isPolling || !query._enableSmartPolling) {
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
query.pausePolling();
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
});
|
|
1447
|
+
windowFocus$.subscribe(() => {
|
|
1448
|
+
if (this._config?.enableSmartPolling || this._config?.autoRefreshQueriesOnWindowFocus) {
|
|
1449
|
+
this.forEach((query) => {
|
|
1450
|
+
if (this._config?.enableSmartPolling && query._isPollingPaused) {
|
|
1451
|
+
query.resumePolling();
|
|
1452
|
+
}
|
|
1453
|
+
if (this._config?.autoRefreshQueriesOnWindowFocus &&
|
|
1454
|
+
query.isExpired &&
|
|
1455
|
+
query.isInUse &&
|
|
1456
|
+
query.autoRefreshOnConfig.windowFocus) {
|
|
1457
|
+
query.execute({ skipCache: true, _triggeredVia: 'auto' });
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
this._initGarbageCollector();
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1382
1464
|
_logState(key, item, operation) {
|
|
1383
1465
|
if (!this._config?.enableChangeLogging)
|
|
1384
1466
|
return;
|
|
@@ -1538,6 +1620,8 @@ class QueryClient {
|
|
|
1538
1620
|
this._store = new QueryStore({
|
|
1539
1621
|
enableChangeLogging: _clientConfig.logging?.queryStateChanges,
|
|
1540
1622
|
enableGarbageCollectorLogging: _clientConfig.logging?.queryStateGarbageCollector,
|
|
1623
|
+
autoRefreshQueriesOnWindowFocus: _clientConfig.request?.autoRefreshQueriesOnWindowFocus ?? true,
|
|
1624
|
+
enableSmartPolling: _clientConfig.request?.enableSmartPolling ?? true,
|
|
1541
1625
|
});
|
|
1542
1626
|
}
|
|
1543
1627
|
}
|