@ethlete/query 1.0.1 → 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.
- 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 +52 -7
- package/esm2020/lib/query/query.types.mjs +1 -1
- package/esm2020/lib/query-client/query-client.mjs +12 -4
- package/esm2020/lib/query-client/query-client.types.mjs +1 -1
- package/esm2020/lib/query-store/query-store.mjs +53 -2
- package/fesm2015/ethlete-query.mjs +151 -33
- package/fesm2015/ethlete-query.mjs.map +1 -1
- package/fesm2020/ethlete-query.mjs +136 -25
- 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 +7 -1
- package/lib/query/query.types.d.ts +48 -4
- package/lib/query-client/query-client.d.ts +4 -1
- package/lib/query-client/query-client.types.d.ts +11 -0
- package/lib/query-store/query-store.d.ts +8 -0
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getCookie, setCookie, deleteCookie, DestroyService, equal, clone } from '@ethlete/core';
|
|
2
|
-
import { Observable, filter, takeWhile, switchMap, of, BehaviorSubject, Subject, takeUntil, interval, tap, timer, fromEvent,
|
|
1
|
+
import { BehaviorSubjectWithSubscriberCount, getCookie, setCookie, deleteCookie, DestroyService, equal, clone } from '@ethlete/core';
|
|
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';
|
|
@@ -545,6 +545,26 @@ class Query {
|
|
|
545
545
|
}
|
|
546
546
|
return ts < Date.now();
|
|
547
547
|
}
|
|
548
|
+
get isInUse() {
|
|
549
|
+
return this._state$.subscriberCount > 0;
|
|
550
|
+
}
|
|
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;
|
|
567
|
+
}
|
|
548
568
|
constructor(_client, _queryConfig, _routeWithParams, _args) {
|
|
549
569
|
this._client = _client;
|
|
550
570
|
this._queryConfig = _queryConfig;
|
|
@@ -553,15 +573,21 @@ class Query {
|
|
|
553
573
|
this._currentId = 0;
|
|
554
574
|
this._pollingSubscription = null;
|
|
555
575
|
this._onAbort$ = new Subject();
|
|
556
|
-
this.
|
|
576
|
+
this._currentPollConfig = null;
|
|
577
|
+
/**
|
|
578
|
+
* @internal
|
|
579
|
+
*/
|
|
580
|
+
this._isPollingPaused = false;
|
|
581
|
+
this._state$ = new BehaviorSubjectWithSubscriberCount({
|
|
557
582
|
type: "PREPARED" /* QueryStateType.Prepared */,
|
|
558
|
-
meta: { id: this._currentId },
|
|
583
|
+
meta: { id: this._currentId, triggeredVia: 'program' },
|
|
559
584
|
});
|
|
560
585
|
}
|
|
561
586
|
clone() {
|
|
562
587
|
return this._client.fetch(this._queryConfig);
|
|
563
588
|
}
|
|
564
589
|
execute(options) {
|
|
590
|
+
const triggeredVia = options?._triggeredVia ?? 'program';
|
|
565
591
|
if (!this.isExpired && !options?.skipCache && isQueryStateSuccess(this.state)) {
|
|
566
592
|
return this;
|
|
567
593
|
}
|
|
@@ -572,7 +598,7 @@ class Query {
|
|
|
572
598
|
if (isQueryStateLoading(this._state$.value)) {
|
|
573
599
|
this.abort();
|
|
574
600
|
}
|
|
575
|
-
const meta = { id };
|
|
601
|
+
const meta = { id, triggeredVia };
|
|
576
602
|
this._state$.next({
|
|
577
603
|
type: "LOADING" /* QueryStateType.Loading */,
|
|
578
604
|
meta,
|
|
@@ -682,16 +708,34 @@ class Query {
|
|
|
682
708
|
if (this._pollingSubscription) {
|
|
683
709
|
return this;
|
|
684
710
|
}
|
|
685
|
-
this.
|
|
711
|
+
this._currentPollConfig = config;
|
|
712
|
+
const _interval = config.triggerImmediately
|
|
713
|
+
? interval(config.interval).pipe(startWith(-1))
|
|
714
|
+
: interval(config.interval);
|
|
715
|
+
this._pollingSubscription = _interval
|
|
686
716
|
.pipe(takeUntil(config.takeUntil))
|
|
687
|
-
.subscribe(() => this.execute({ skipCache: true }));
|
|
717
|
+
.subscribe(() => this.execute({ skipCache: true, _triggeredVia: 'poll' }));
|
|
688
718
|
return this;
|
|
689
719
|
}
|
|
690
720
|
stopPolling() {
|
|
691
721
|
this._pollingSubscription?.unsubscribe();
|
|
692
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;
|
|
693
730
|
return this;
|
|
694
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
|
+
}
|
|
695
739
|
_updateUseResultInDependencies() {
|
|
696
740
|
if (!this._args?.useResultIn?.length) {
|
|
697
741
|
return;
|
|
@@ -957,9 +1001,9 @@ class QueryDirective {
|
|
|
957
1001
|
}
|
|
958
1002
|
}
|
|
959
1003
|
}
|
|
960
|
-
QueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1
|
|
961
|
-
QueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1
|
|
962
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1
|
|
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: [{
|
|
963
1007
|
type: Directive,
|
|
964
1008
|
args: [{
|
|
965
1009
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
@@ -1032,6 +1076,7 @@ class InfinityQuery {
|
|
|
1032
1076
|
console.error('Cannot load more pages, already at the end. Make sure to not render the infinity query trigger using *ngIf canLoadMore');
|
|
1033
1077
|
return;
|
|
1034
1078
|
}
|
|
1079
|
+
this._clearSubscriptions();
|
|
1035
1080
|
const args = this._prepareArgs(this._config, calculatedPage);
|
|
1036
1081
|
const query = this._config.queryCreator.prepare(args).execute();
|
|
1037
1082
|
this._handleNewQuery(query);
|
|
@@ -1040,7 +1085,7 @@ class InfinityQuery {
|
|
|
1040
1085
|
this._currentQuery$.next(query);
|
|
1041
1086
|
}
|
|
1042
1087
|
reset(newConfig) {
|
|
1043
|
-
this.
|
|
1088
|
+
this._clearSubscriptions();
|
|
1044
1089
|
this._config = { ...this._config, ...(newConfig ?? {}) };
|
|
1045
1090
|
this._currentPage$.next(null);
|
|
1046
1091
|
this._currentCalculatedPage$.next(null);
|
|
@@ -1048,8 +1093,9 @@ class InfinityQuery {
|
|
|
1048
1093
|
this._itemsPerPage$.next(this._config.limitParam?.value ?? 10);
|
|
1049
1094
|
this._currentQuery$.next(null);
|
|
1050
1095
|
this._data$.next([]);
|
|
1096
|
+
this.nextPage();
|
|
1051
1097
|
}
|
|
1052
|
-
|
|
1098
|
+
_clearSubscriptions() {
|
|
1053
1099
|
this._subscriptions.forEach((sub) => sub.unsubscribe());
|
|
1054
1100
|
this._subscriptions = [];
|
|
1055
1101
|
}
|
|
@@ -1079,7 +1125,14 @@ class InfinityQuery {
|
|
|
1079
1125
|
this._subscriptions = this._subscriptions.filter((sub) => sub !== stateChangesSub);
|
|
1080
1126
|
},
|
|
1081
1127
|
});
|
|
1082
|
-
|
|
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);
|
|
1083
1136
|
}
|
|
1084
1137
|
_prepareArgs(config, page) {
|
|
1085
1138
|
const pageParamLocation = this._getPageParamLocation(config?.pageParam?.location);
|
|
@@ -1193,7 +1246,7 @@ class InfinityQueryDirective {
|
|
|
1193
1246
|
this._renderMainView();
|
|
1194
1247
|
}
|
|
1195
1248
|
ngOnDestroy() {
|
|
1196
|
-
this.instance?.
|
|
1249
|
+
this.instance?._clearSubscriptions();
|
|
1197
1250
|
}
|
|
1198
1251
|
_setupInfinityQuery(config) {
|
|
1199
1252
|
const instance = new InfinityQuery(config);
|
|
@@ -1259,7 +1312,7 @@ class InfinityQueryDirective {
|
|
|
1259
1312
|
}
|
|
1260
1313
|
_cleanup() {
|
|
1261
1314
|
this._queryConfigChanged$.next(true);
|
|
1262
|
-
this.instance?.
|
|
1315
|
+
this.instance?._clearSubscriptions();
|
|
1263
1316
|
this._viewContext.loading = false;
|
|
1264
1317
|
this._viewContext.error = null;
|
|
1265
1318
|
this._viewContext.infinityQuery = null;
|
|
@@ -1272,9 +1325,9 @@ class InfinityQueryDirective {
|
|
|
1272
1325
|
this._data$.next([]);
|
|
1273
1326
|
}
|
|
1274
1327
|
}
|
|
1275
|
-
InfinityQueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1
|
|
1276
|
-
InfinityQueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1
|
|
1277
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1
|
|
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: [{
|
|
1278
1331
|
type: Directive,
|
|
1279
1332
|
args: [{
|
|
1280
1333
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
@@ -1324,9 +1377,9 @@ class InfinityQueryTriggerDirective {
|
|
|
1324
1377
|
this._observer.observe(this._elementRef.nativeElement);
|
|
1325
1378
|
}
|
|
1326
1379
|
}
|
|
1327
|
-
InfinityQueryTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1
|
|
1328
|
-
InfinityQueryTriggerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1
|
|
1329
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1
|
|
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: [{
|
|
1330
1383
|
type: Directive,
|
|
1331
1384
|
args: [{
|
|
1332
1385
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
@@ -1342,6 +1395,7 @@ class QueryStore {
|
|
|
1342
1395
|
this._config = _config;
|
|
1343
1396
|
this._store = new Map();
|
|
1344
1397
|
this._garbageCollector = null;
|
|
1398
|
+
this._initSmartQueryHandling();
|
|
1345
1399
|
}
|
|
1346
1400
|
add(id, query) {
|
|
1347
1401
|
this._store.set(id, query);
|
|
@@ -1355,6 +1409,55 @@ class QueryStore {
|
|
|
1355
1409
|
this._store.delete(id);
|
|
1356
1410
|
this._logState(id, null, 'REMOVE');
|
|
1357
1411
|
}
|
|
1412
|
+
forEach(callback) {
|
|
1413
|
+
for (const [key, query] of this._store) {
|
|
1414
|
+
callback(query, key);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
refreshQueriesInUse(config) {
|
|
1418
|
+
const { ignoreCacheValidity, purgeUnused } = config ?? {};
|
|
1419
|
+
for (const [key, query] of this._store) {
|
|
1420
|
+
if (query.isInUse &&
|
|
1421
|
+
(query.isExpired || ignoreCacheValidity) &&
|
|
1422
|
+
query.autoRefreshOnConfig.queryClientDefaultHeadersChange) {
|
|
1423
|
+
query.execute({ skipCache: true, _triggeredVia: 'auto' });
|
|
1424
|
+
}
|
|
1425
|
+
else if (purgeUnused && !query.isInUse) {
|
|
1426
|
+
this.remove(key);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
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
|
+
}
|
|
1358
1461
|
_logState(key, item, operation) {
|
|
1359
1462
|
if (!this._config?.enableChangeLogging)
|
|
1360
1463
|
return;
|
|
@@ -1384,7 +1487,7 @@ class QueryStore {
|
|
|
1384
1487
|
_runGarbageCollector() {
|
|
1385
1488
|
this._logGarbageCollector('Collecting...');
|
|
1386
1489
|
this._store.forEach((item, key) => {
|
|
1387
|
-
if (item.isExpired) {
|
|
1490
|
+
if (item.isExpired && !item.isInUse) {
|
|
1388
1491
|
this.remove(key);
|
|
1389
1492
|
}
|
|
1390
1493
|
});
|
|
@@ -1470,6 +1573,9 @@ class QueryClient {
|
|
|
1470
1573
|
if (shouldCacheQuery(queryConfig.method)) {
|
|
1471
1574
|
const existingQuery = this._store.get(cacheKey);
|
|
1472
1575
|
if (existingQuery) {
|
|
1576
|
+
if (existingQuery.isExpired) {
|
|
1577
|
+
existingQuery.execute();
|
|
1578
|
+
}
|
|
1473
1579
|
return existingQuery;
|
|
1474
1580
|
}
|
|
1475
1581
|
}
|
|
@@ -1491,14 +1597,17 @@ class QueryClient {
|
|
|
1491
1597
|
}
|
|
1492
1598
|
this._authProvider$.next(authProvider);
|
|
1493
1599
|
};
|
|
1494
|
-
this.setDefaultHeaders = (
|
|
1600
|
+
this.setDefaultHeaders = (config) => {
|
|
1495
1601
|
if (!this._clientConfig.request) {
|
|
1496
1602
|
this._clientConfig.request = {
|
|
1497
|
-
headers: headers ?? undefined,
|
|
1603
|
+
headers: config.headers ?? undefined,
|
|
1498
1604
|
};
|
|
1499
1605
|
}
|
|
1500
1606
|
else {
|
|
1501
|
-
this._clientConfig.request.headers = headers ?? undefined;
|
|
1607
|
+
this._clientConfig.request.headers = config.headers ?? undefined;
|
|
1608
|
+
}
|
|
1609
|
+
if (config.refreshQueriesInUse) {
|
|
1610
|
+
this._store.refreshQueriesInUse({ purgeUnused: true, ignoreCacheValidity: true });
|
|
1502
1611
|
}
|
|
1503
1612
|
};
|
|
1504
1613
|
this.clearAuthProvider = () => {
|
|
@@ -1508,6 +1617,8 @@ class QueryClient {
|
|
|
1508
1617
|
this._store = new QueryStore({
|
|
1509
1618
|
enableChangeLogging: _clientConfig.logging?.queryStateChanges,
|
|
1510
1619
|
enableGarbageCollectorLogging: _clientConfig.logging?.queryStateGarbageCollector,
|
|
1620
|
+
autoRefreshQueriesOnWindowFocus: _clientConfig.request?.autoRefreshQueriesOnWindowFocus ?? true,
|
|
1621
|
+
enableSmartPolling: _clientConfig.request?.enableSmartPolling ?? true,
|
|
1511
1622
|
});
|
|
1512
1623
|
}
|
|
1513
1624
|
}
|