\ No newline at end of file
diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.ts b/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.ts
index 79bb6cb74c..7552b1a9d3 100644
--- a/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.ts
+++ b/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.ts
@@ -2,15 +2,13 @@ import { animate, query, style, transition, trigger } from '@angular/animations'
import { Component, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';
-import { Observable, Subscription } from 'rxjs';
+import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state';
-import { applicationEntityType } from '../../../../../cloud-foundry/src/cf-entity-types';
import { ListConfig } from '../../../../../core/src/shared/components/list/list.component.types';
import { CfAppConfigService } from '../../../shared/components/list/list-types/app/cf-app-config.service';
-import { CfAppsDataSource } from '../../../shared/components/list/list-types/app/cf-apps-data-source';
-import { CfOrgSpaceDataService, initCfOrgSpaceService } from '../../../shared/data-services/cf-org-space-service.service';
+import { CfOrgSpaceDataService } from '../../../shared/data-services/cf-org-space-service.service';
import { CloudFoundryService } from '../../../shared/data-services/cloud-foundry.service';
import { CfCurrentUserPermissions } from '../../../user-permissions/cf-user-permissions-checkers';
import { goToAppWall } from '../../cf/cf.helpers';
@@ -22,13 +20,13 @@ import { goToAppWall } from '../../cf/cf.helpers';
animations: [
trigger(
'cardEnter', [
- transition('* => *', [
- query(':enter', [
- style({ opacity: 0, transform: 'translateY(10px)' }),
- animate('150ms ease-out', style({ opacity: 1, transform: 'translateY(0)' }))
- ], { optional: true })
- ])
- ]
+ transition('* => *', [
+ query(':enter', [
+ style({ opacity: 0, transform: 'translateY(10px)' }),
+ animate('150ms ease-out', style({ opacity: 1, transform: 'translateY(0)' }))
+ ], { optional: true })
+ ])
+ ]
)
],
providers: [{
@@ -40,7 +38,6 @@ import { goToAppWall } from '../../cf/cf.helpers';
})
export class ApplicationWallComponent implements OnDestroy {
public cfIds$: Observable;
- private initCfOrgSpaceService: Subscription;
public canCreateApplication: string;
@@ -49,7 +46,7 @@ export class ApplicationWallComponent implements OnDestroy {
constructor(
public cloudFoundryService: CloudFoundryService,
private store: Store,
- private cfOrgSpaceService: CfOrgSpaceDataService,
+ public cfOrgSpaceService: CfOrgSpaceDataService,
activatedRoute: ActivatedRoute,
) {
// If we have an endpoint ID, select it and redirect
@@ -67,16 +64,8 @@ export class ApplicationWallComponent implements OnDestroy {
this.haveConnectedCf$ = cloudFoundryService.connectedCFEndpoints$.pipe(
map(endpoints => !!endpoints && endpoints.length > 0)
);
-
- this.initCfOrgSpaceService = initCfOrgSpaceService(this.store,
- this.cfOrgSpaceService,
- applicationEntityType,
- CfAppsDataSource.paginationKey).subscribe();
}
ngOnDestroy(): void {
- if (this.initCfOrgSpaceService) {
- this.initCfOrgSpaceService.unsubscribe();
- }
}
}
diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/create-application/create-application.component.ts b/src/frontend/packages/cloud-foundry/src/features/applications/create-application/create-application.component.ts
index 438d60af57..48e54d2ce1 100644
--- a/src/frontend/packages/cloud-foundry/src/features/applications/create-application/create-application.component.ts
+++ b/src/frontend/packages/cloud-foundry/src/features/applications/create-application/create-application.component.ts
@@ -4,10 +4,10 @@ import { Subscription } from 'rxjs';
import { filter, first, tap } from 'rxjs/operators';
import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state';
-import { applicationEntityType } from '../../../../../cloud-foundry/src/cf-entity-types';
-import { selectCfPaginationState } from '../../../../../cloud-foundry/src/store/selectors/pagination.selectors';
+import { applicationEntityType } from '../../../cf-entity-types';
import { CfAppsDataSource } from '../../../shared/components/list/list-types/app/cf-apps-data-source';
import { CfOrgSpaceDataService } from '../../../shared/data-services/cf-org-space-service.service';
+import { selectCfPaginationState } from '../../../store/selectors/pagination.selectors';
@Component({
selector: 'app-create-application',
@@ -22,6 +22,9 @@ export class CreateApplicationComponent implements OnInit, OnDestroy {
ngOnInit() {
// We will auto select endpoint/org/space that have been selected on the app wall.
+ this.cfOrgSpaceService.enableAutoSelectors();
+ // FIXME: This has been broken for a while (setting cf will clear org + space after org and space has been set)
+ // With new tools (set initial/enable auto) this should be easier to fix
const appWallPaginationState = this.store.select(selectCfPaginationState(applicationEntityType, CfAppsDataSource.paginationKey));
this.paginationStateSub = appWallPaginationState.pipe(filter(pag => !!pag), first(), tap(pag => {
const { cf, org, space } = pag.clientPagination.filter.items;
@@ -29,7 +32,6 @@ export class CreateApplicationComponent implements OnInit, OnDestroy {
this.cfOrgSpaceService.cf.select.next(cf);
}
if (cf && org) {
-
this.cfOrgSpaceService.org.select.next(org);
}
if (cf && org && space) {
diff --git a/src/frontend/packages/cloud-foundry/src/features/services/services-wall/services-wall.component.html b/src/frontend/packages/cloud-foundry/src/features/services/services-wall/services-wall.component.html
index b47b7c7e12..d188fc7735 100644
--- a/src/frontend/packages/cloud-foundry/src/features/services/services-wall/services-wall.component.html
+++ b/src/frontend/packages/cloud-foundry/src/features/services/services-wall/services-wall.component.html
@@ -26,5 +26,6 @@
Services
\ No newline at end of file
diff --git a/src/frontend/packages/cloud-foundry/src/features/services/services-wall/services-wall.component.ts b/src/frontend/packages/cloud-foundry/src/features/services/services-wall/services-wall.component.ts
index 3f30841d43..05f0c8b613 100644
--- a/src/frontend/packages/cloud-foundry/src/features/services/services-wall/services-wall.component.ts
+++ b/src/frontend/packages/cloud-foundry/src/features/services/services-wall/services-wall.component.ts
@@ -1,17 +1,13 @@
-import { Component, OnDestroy } from '@angular/core';
+import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state';
-import { serviceInstancesEntityType } from '../../../../../cloud-foundry/src/cf-entity-types';
import {
ServiceInstancesWallListConfigService,
} from '../../../../../cloud-foundry/src/shared/components/list/list-types/services-wall/service-instances-wall-list-config.service';
-import {
- CfOrgSpaceDataService,
- initCfOrgSpaceService,
-} from '../../../../../cloud-foundry/src/shared/data-services/cf-org-space-service.service';
+import { CfOrgSpaceDataService } from '../../../../../cloud-foundry/src/shared/data-services/cf-org-space-service.service';
import { CloudFoundryService } from '../../../../../cloud-foundry/src/shared/data-services/cloud-foundry.service';
import { ListConfig } from '../../../../../core/src/shared/components/list/list.component.types';
import { CSI_CANCEL_URL } from '../../../shared/components/add-service-instance/csi-mode.service';
@@ -29,7 +25,7 @@ import { CfCurrentUserPermissions } from '../../../user-permissions/cf-user-perm
CfOrgSpaceDataService
]
})
-export class ServicesWallComponent implements OnDestroy {
+export class ServicesWallComponent {
public haveConnectedCf$: Observable;
@@ -41,7 +37,7 @@ export class ServicesWallComponent implements OnDestroy {
constructor(
public cloudFoundryService: CloudFoundryService,
public store: Store,
- private cfOrgSpaceService: CfOrgSpaceDataService) {
+ public cfOrgSpaceService: CfOrgSpaceDataService) {
this.canCreateServiceInstance = CfCurrentUserPermissions.SERVICE_INSTANCE_CREATE;
this.cfIds$ = cloudFoundryService.cFEndpoints$.pipe(
@@ -51,11 +47,6 @@ export class ServicesWallComponent implements OnDestroy {
)
);
- this.initCfOrgSpaceService = initCfOrgSpaceService(this.store,
- this.cfOrgSpaceService,
- serviceInstancesEntityType,
- 'all').subscribe();
-
this.haveConnectedCf$ = cloudFoundryService.connectedCFEndpoints$.pipe(
map(endpoints => !!endpoints && endpoints.length > 0)
);
@@ -64,8 +55,4 @@ export class ServicesWallComponent implements OnDestroy {
[CSI_CANCEL_URL]: `/services`
};
}
-
- ngOnDestroy(): void {
- this.initCfOrgSpaceService.unsubscribe();
- }
}
diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/create-application/create-application-step1/create-application-step1.component.ts b/src/frontend/packages/cloud-foundry/src/shared/components/create-application/create-application-step1/create-application-step1.component.ts
index 1e3dd731a4..1552c10c08 100644
--- a/src/frontend/packages/cloud-foundry/src/shared/components/create-application/create-application-step1/create-application-step1.component.ts
+++ b/src/frontend/packages/cloud-foundry/src/shared/components/create-application/create-application-step1/create-application-step1.component.ts
@@ -49,7 +49,7 @@ export class CreateApplicationStep1Component implements OnInit, AfterContentInit
space: this.cfOrgSpaceService.space.select.getValue()
}));
return of({ success: true });
- }
+ };
ngOnInit() {
if (this.route.root.snapshot.queryParams.endpointGuid) {
diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/cf-app-config.service.ts b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/cf-app-config.service.ts
index a2b5398834..81e7adea09 100644
--- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/cf-app-config.service.ts
+++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/cf-app-config.service.ts
@@ -48,13 +48,14 @@ export class CfAppConfigService extends ListConfig implements IList
// Apply the initial cf guid to the data source. Normally this is done via applying the selection to the filter... however this is too
// late for maxedResult world
- this.initialised$ = this.cfOrgSpaceService.cf.loading$.pipe(
+ this.initialised$ = this.cfOrgSpaceService.isLoading$.pipe(
filter(isLoading => !isLoading),
switchMap(() => this.cfOrgSpaceService.cf.list$),
first(),
map(cfs => {
const cfGuid = cfs.length === 1 ? cfs[0].guid : null;
this.appsDataSource = new CfAppsDataSource(this.store, this, undefined, undefined, undefined, cfGuid);
+ this.cfOrgSpaceService.setInitialValuesFromAction(this.appsDataSource.action, 'cf', 'org', 'space');
return true;
})
);
diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-routes/cf-routes-list-config.service.ts b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-routes/cf-routes-list-config.service.ts
index f800809ecf..1b640b3a54 100644
--- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-routes/cf-routes-list-config.service.ts
+++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-routes/cf-routes-list-config.service.ts
@@ -16,11 +16,7 @@ import {
import { APIResource } from '../../../../../../../store/src/types/api.types';
import { CloudFoundryEndpointService } from '../../../../../features/cf/services/cloud-foundry-endpoint.service';
import { CfCurrentUserPermissions } from '../../../../../user-permissions/cf-user-permissions-checkers';
-import {
- CfOrgSpaceDataService,
- createCfOrgSpaceFilterConfig,
- initCfOrgSpaceService,
-} from '../../../../data-services/cf-org-space-service.service';
+import { CfOrgSpaceDataService, createCfOrgSpaceFilterConfig } from '../../../../data-services/cf-org-space-service.service';
import { CfRoutesDataSource } from './cf-routes-data-source';
import { ListCfRoute } from './cf-routes-data-source-base';
import { CfRoutesListConfigBase } from './cf-routes-list-config-base';
@@ -76,10 +72,6 @@ export class CfRoutesListConfigService extends CfRoutesListConfigBase implements
createCfOrgSpaceFilterConfig('org', 'Organization', cfOrgSpaceService.org),
];
this.getMultiFiltersConfigs = () => multiFilterConfigs;
- initCfOrgSpaceService(store, cfOrgSpaceService,
- this.dataSource.masterAction.entityType,
- this.dataSource.masterAction.paginationKey).subscribe();
- cfOrgSpaceService.cf.select.next(cfService.cfGuid);
this.getInitialised = () => combineLatest(
cfOrgSpaceService.cf.list$,
@@ -89,5 +81,7 @@ export class CfRoutesListConfigService extends CfRoutesListConfigBase implements
map(loading => !loading),
startWith(true)
);
+
+ cfOrgSpaceService.cf.select.next(cfService.cfGuid);
}
}
diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/services-wall/service-instances-wall-list-config.service.ts b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/services-wall/service-instances-wall-list-config.service.ts
index 2c76635469..3f327ab0b7 100644
--- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/services-wall/service-instances-wall-list-config.service.ts
+++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/services-wall/service-instances-wall-list-config.service.ts
@@ -91,6 +91,7 @@ export class ServiceInstancesWallListConfigService extends CfServiceInstancesLis
breadcrumbs: 'service-wall'
};
+ this.cfOrgSpaceService.setInitialValuesFromAction(this.dataSource.masterAction, 'cf', 'org', 'space');
this.getInitialised = () => combineLatest(
cfOrgSpaceService.cf.list$,
cfOrgSpaceService.org.list$,
diff --git a/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-org-space-service.service.ts b/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-org-space-service.service.ts
index e8ce2c3184..188cb717af 100644
--- a/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-org-space-service.service.ts
+++ b/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-org-space-service.service.ts
@@ -1,6 +1,6 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Store } from '@ngrx/store';
-import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
+import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs';
import {
distinctUntilChanged,
filter,
@@ -28,10 +28,9 @@ import { PaginationMonitorFactory } from '../../../../store/src/monitors/paginat
import { getPaginationObservables } from '../../../../store/src/reducers/pagination-reducer/pagination-reducer.helper';
import { getCurrentPageRequestInfo } from '../../../../store/src/reducers/pagination-reducer/pagination-reducer.types';
import { connectedEndpointsOfTypesSelector } from '../../../../store/src/selectors/endpoint.selectors';
-import { selectPaginationState } from '../../../../store/src/selectors/pagination.selectors';
import { APIResource } from '../../../../store/src/types/api.types';
import { EndpointModel } from '../../../../store/src/types/endpoint.types';
-import { PaginatedAction, PaginationParam } from '../../../../store/src/types/pagination.types';
+import { PaginatedAction, PaginationEntityState, PaginationParam } from '../../../../store/src/types/pagination.types';
import { IOrganization, ISpace } from '../../cf-api.types';
import { cfEntityCatalog } from '../../cf-entity-catalog';
import { cfEntityFactory } from '../../cf-entity-factory';
@@ -63,6 +62,8 @@ export function createCfOrgSpaceFilterConfig(key: string, label: string, cfOrgSp
export interface CfOrgSpaceItem {
list$: Observable;
loading$: Observable;
+ // A lot of problems are caused by these being BehaviourSubject's (specifically auto select process in CfOrgSpaceDataService and sticky
+ // values). Ideally this would change to Subject... but some usages .value
select: BehaviorSubject;
}
@@ -77,30 +78,6 @@ export const enum CfOrgSpaceSelectMode {
ANY = 2
}
-
-export const initCfOrgSpaceService = (
- store: Store,
- cfOrgSpaceService: CfOrgSpaceDataService,
- entityKey: string,
- paginationKey: string): Observable => {
- return store.select(selectPaginationState(entityKey, paginationKey)).pipe(
- filter((pag) => !!pag),
- first(),
- tap(pag => {
- const { cf, org, space } = pag.clientPagination.filter.items;
- if (cf) {
- cfOrgSpaceService.cf.select.next(cf);
- }
- if (org) {
- cfOrgSpaceService.org.select.next(org);
- }
- if (space) {
- cfOrgSpaceService.space.select.next(space);
- }
- })
- );
-};
-
export const createCfOrSpaceMultipleFilterFn = (
store: Store,
action: PaginatedAction,
@@ -154,6 +131,7 @@ export const createCfOrSpaceMultipleFilterFn = (
};
};
+interface InitialValues { cf: string; org: string; space: string; }
/**
* This service relies on OnDestroy, so must be `provided` by a component
@@ -168,7 +146,7 @@ export class CfOrgSpaceDataService implements OnDestroy {
public space: CfOrgSpaceItem;
public isLoading$: Observable;
- public paginationAction = this.createPaginationAction();
+ public paginationAction = this.createOrgPaginationAction();
/**
* This will contain all org and space data
@@ -182,6 +160,15 @@ export class CfOrgSpaceDataService implements OnDestroy {
private selectMode = CfOrgSpaceSelectMode.FIRST_ONLY;
private subs: Subscription[] = [];
+ /*
+ * Observable that provides initial values for drop downs, output will be parsed through initialValuesMap before emitted on first
+ */
+ public initialValues$: Observable;
+ /**
+ * Map values from `initialValues$` to supply initial values for drop downs
+ */
+ public initialValuesMap: (param: any) => InitialValues;
+
constructor(
private store: Store,
public paginationMonitorFactory: PaginationMonitorFactory,
@@ -190,15 +177,6 @@ export class CfOrgSpaceDataService implements OnDestroy {
this.createOrg();
this.createSpace();
- // Start watching the cf/org/space plus automatically setting values only when we actually have values to auto select
- this.org.list$.pipe(
- first(),
- ).subscribe({
- complete: () => {
- this.setupAutoSelectors();
- }
- });
-
this.isLoading$ = combineLatest(
this.cf.loading$,
this.org.loading$,
@@ -249,7 +227,7 @@ export class CfOrgSpaceDataService implements OnDestroy {
loading$: list$.pipe(
map(cfs => !cfs)
),
- select: new BehaviorSubject(undefined)
+ select: new BehaviorSubject(null) // Should be different to undefined (sticky values & reset list)
};
}
@@ -270,7 +248,7 @@ export class CfOrgSpaceDataService implements OnDestroy {
this.org = {
list$: orgList$,
loading$: this.allOrgsLoading$,
- select: new BehaviorSubject(undefined)
+ select: new BehaviorSubject(null) // Should be different to undefined (sticky values & reset list)
};
}
@@ -297,11 +275,11 @@ export class CfOrgSpaceDataService implements OnDestroy {
this.space = {
list$: spaceList$,
loading$: this.org.loading$,
- select: new BehaviorSubject(undefined)
+ select: new BehaviorSubject(null) // Should be different to undefined (sticky values & reset list)
};
}
- private createPaginationAction() {
+ private createOrgPaginationAction() {
return cfEntityCatalog.org.actions.getMultiple(null, CfOrgSpaceDataService.CfOrgSpaceServicePaginationKey, {
includeRelations: [
createEntityRelationKey(organizationEntityType, spaceEntityType),
@@ -318,12 +296,57 @@ export class CfOrgSpaceDataService implements OnDestroy {
);
}
- private setupAutoSelectors() {
+ public setInitialValuesFromAction(
+ paginatedAction: PaginatedAction,
+ cfKey: string,
+ orgKey: string,
+ spaceKey: string,
+ ) {
+ this.initialValuesMap = (p: PaginationEntityState) => ({
+ cf: p.clientPagination?.filter?.items[cfKey],
+ org: p.clientPagination?.filter?.items[orgKey],
+ space: p.clientPagination?.filter?.items[spaceKey]
+ });
+ this.initialValues$ = this.paginationMonitorFactory.create(
+ paginatedAction.paginationKey,
+ cfEntityFactory(paginatedAction.entityType),
+ paginatedAction.flattenPagination
+ ).pagination$.pipe(
+ filter(p => !!p?.clientPagination?.filter),
+ );
+ }
+
+ private getInitialValues(): Observable {
+ const initialValues$ = this.initialValues$ || of({ cf: undefined, org: undefined, space: undefined });
+ const defaultMap = (a: any) => a;
+ const initialValuesMap = this.initialValuesMap || defaultMap;
+ return initialValues$.pipe(
+ first(),
+ map(initialValuesMap) // Map needs to happen at the point the auto selectors are enabled
+ );
+ }
+
+ public enableAutoSelectors() {
+ combineLatest(
+ // Start watching the cf/org/space plus automatically setting values only when we actually have values to auto select
+ this.org.list$,
+ // Get initial values only after we've given a prod... so first values emitted are the one's we want
+ this.getInitialValues(),
+ ).pipe(first()).subscribe(([, initialValues]) => {
+ this.setupAutoSelectors(initialValues.cf, initialValues.org);
+ });
+ }
+
+ private setupAutoSelectors(initialCf: string, initialOrg: string) {
+ // Clear or automatically select org + space given cf
+ let cfTapped = false;
const orgResetSub = this.cf.select.asObservable().pipe(
- startWith(undefined),
+ startWith(initialCf),
distinctUntilChanged(),
+ filter(cf => cfTapped || cf !== initialCf),
withLatestFrom(this.org.list$),
tap(([selectedCF, orgs]) => {
+ cfTapped = true;
if (
!!orgs.length &&
((this.selectMode === CfOrgSpaceSelectMode.FIRST_ONLY && orgs.length === 1) ||
@@ -339,11 +362,14 @@ export class CfOrgSpaceDataService implements OnDestroy {
this.subs.push(orgResetSub);
// Clear or automatically select space given org
+ let orgTapped = false;
const spaceResetSub = this.org.select.asObservable().pipe(
- startWith(undefined),
+ startWith(initialOrg),
distinctUntilChanged(),
+ filter(org => orgTapped || org !== initialOrg),
withLatestFrom(this.space.list$),
tap(([selectedOrg, spaces]) => {
+ orgTapped = true;
if (
!!spaces.length &&
((this.selectMode === CfOrgSpaceSelectMode.FIRST_ONLY && spaces.length === 1) ||
diff --git a/src/frontend/packages/cloud-foundry/src/shared/services/current-user-permissions.service.spec.ts b/src/frontend/packages/cloud-foundry/src/shared/services/current-user-permissions-and-cfchecker.service.spec.ts
similarity index 98%
rename from src/frontend/packages/cloud-foundry/src/shared/services/current-user-permissions.service.spec.ts
rename to src/frontend/packages/cloud-foundry/src/shared/services/current-user-permissions-and-cfchecker.service.spec.ts
index ddf7d54350..0852f7e8b2 100644
--- a/src/frontend/packages/cloud-foundry/src/shared/services/current-user-permissions.service.spec.ts
+++ b/src/frontend/packages/cloud-foundry/src/shared/services/current-user-permissions-and-cfchecker.service.spec.ts
@@ -2,16 +2,6 @@ import { TestBed } from '@angular/core/testing';
import { createBasicStoreModule, createEntityStoreState, TestStoreEntity } from '@stratosui/store/testing';
import { first, tap } from 'rxjs/operators';
-import { CFFeatureFlagTypes, IFeatureFlag } from '../../../../cloud-foundry/src/cf-api.types';
-import { cfEntityFactory } from '../../../../cloud-foundry/src/cf-entity-factory';
-import { generateCFEntities } from '../../../../cloud-foundry/src/cf-entity-generator';
-import { featureFlagEntityType } from '../../../../cloud-foundry/src/cf-entity-types';
-import {
- CfCurrentUserPermissions,
- cfCurrentUserPermissionsService,
- CfPermissionTypes,
- CfScopeStrings,
-} from '../../../../cloud-foundry/src/user-permissions/cf-user-permissions-checkers';
import { PermissionConfig } from '../../../../core/src/core/permissions/current-user-permissions.config';
import { CurrentUserPermissionsService } from '../../../../core/src/core/permissions/current-user-permissions.service';
import { StratosScopeStrings } from '../../../../core/src/core/permissions/stratos-user-permissions.checker';
@@ -25,6 +15,16 @@ import { APIResource } from '../../../../store/src/types/api.types';
import { EndpointModel } from '../../../../store/src/types/endpoint.types';
import { BaseEntityValues } from '../../../../store/src/types/entity.types';
import { PaginationState } from '../../../../store/src/types/pagination.types';
+import { CFFeatureFlagTypes, IFeatureFlag } from '../../cf-api.types';
+import { cfEntityFactory } from '../../cf-entity-factory';
+import { generateCFEntities } from '../../cf-entity-generator';
+import { featureFlagEntityType } from '../../cf-entity-types';
+import {
+ CfCurrentUserPermissions,
+ cfCurrentUserPermissionsService,
+ CfPermissionTypes,
+ CfScopeStrings,
+} from '../../user-permissions/cf-user-permissions-checkers';
const ffSchema = cfEntityFactory(featureFlagEntityType);
@@ -520,7 +520,8 @@ describe('CurrentUserPermissionsService with CF checker', () => {
},
totalResults: 2
},
- maxedState: {}
+ maxedState: {},
+ isListPagination: true
}
},
cfFeatureFlag: {
@@ -548,7 +549,8 @@ describe('CurrentUserPermissionsService with CF checker', () => {
},
totalResults: 13
},
- maxedState: {}
+ maxedState: {},
+ isListPagination: false
},
'endpoint-c80420ca-204b-4879-bf69-b6b7a202ad87': {
pageCount: 1,
@@ -574,7 +576,8 @@ describe('CurrentUserPermissionsService with CF checker', () => {
},
totalResults: 13
},
- maxedState: {}
+ maxedState: {},
+ isListPagination: false
}
},
};
diff --git a/src/frontend/packages/core/src/core/permissions/current-user-permissions.service.spec.ts b/src/frontend/packages/core/src/core/permissions/current-user-permissions.service.spec.ts
index 90d5401db5..9d7f3bb0af 100644
--- a/src/frontend/packages/core/src/core/permissions/current-user-permissions.service.spec.ts
+++ b/src/frontend/packages/core/src/core/permissions/current-user-permissions.service.spec.ts
@@ -124,7 +124,8 @@ describe('CurrentUserPermissionsService', () => {
},
totalResults: 2
},
- maxedState: {}
+ maxedState: {},
+ isListPagination: true
}
},
};
diff --git a/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.html b/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.html
index 8c4cf894bf..462d79f69e 100644
--- a/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.html
+++ b/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.html
@@ -50,7 +50,7 @@
User Profile
- Settings
+ Local Settings
@@ -101,8 +101,8 @@
User Profile
-
- Theme
+
+
Theme
User Profile
+
+
Clear Settings
+
+
+
Your browser's local storage is used to persist local
+ settings
+ This covers the options in this section and some changes to lists such as filters and sorting.
+
+
Size of local storage:
+ ~{{ localStorageSize | bytesToHumanSize }}
+
+
+
diff --git a/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.scss b/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.scss
index ba56f7161a..3be8be5d30 100644
--- a/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.scss
+++ b/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.scss
@@ -49,4 +49,14 @@ $user-profile-avatar-size: 48px;
&__content {
margin: 2% 24px;
}
+
+ &__local-storage {
+ &--div {
+ padding-bottom: 10px;
+ }
+
+ button {
+ margin-left: 15px;
+ }
+ }
}
diff --git a/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.ts b/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.ts
index d696e016dd..5f959bd90b 100644
--- a/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.ts
+++ b/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.ts
@@ -1,10 +1,12 @@
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { combineLatest, Observable } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { filter, first, map } from 'rxjs/operators';
import { SetPollingEnabledAction, SetSessionTimeoutAction } from '../../../../../store/src/actions/dashboard-actions';
-import { DashboardOnlyAppState } from '../../../../../store/src/app-state';
+import { AppState } from '../../../../../store/src/app-state';
+import { LocalStorageService } from '../../../../../store/src/helpers/local-storage-service';
+import { selectSessionData } from '../../../../../store/src/reducers/auth.reducer';
import { selectDashboardState } from '../../../../../store/src/selectors/dashboard.selectors';
import { ThemeService } from '../../../../../store/src/theme.service';
import { UserProfileInfo } from '../../../../../store/src/types/user-profile.types';
@@ -12,6 +14,7 @@ import { CurrentUserPermissionsService } from '../../../core/permissions/current
import { StratosCurrentUserPermissions } from '../../../core/permissions/stratos-user-permissions.checker';
import { UserProfileService } from '../../../core/user-profile.service';
import { UserService } from '../../../core/user.service';
+import { ConfirmationDialogService } from '../../../shared/components/confirmation-dialog.service';
import { SetGravatarEnabledAction } from './../../../../../store/src/actions/dashboard-actions';
@Component({
@@ -21,19 +24,22 @@ import { SetGravatarEnabledAction } from './../../../../../store/src/actions/das
})
export class ProfileInfoComponent {
- public timeoutSession$ = this.store.select(selectDashboardState).pipe(
+ private dashboardState$ = this.store.select(selectDashboardState);
+ private sessionData$ = this.store.select(selectSessionData());
+
+ public timeoutSession$ = this.dashboardState$.pipe(
map(dashboardState => dashboardState.timeoutSession ? 'true' : 'false')
);
- public pollingEnabled$ = this.store.select(selectDashboardState).pipe(
+ public pollingEnabled$ = this.dashboardState$.pipe(
map(dashboardState => dashboardState.pollingEnabled ? 'true' : 'false')
);
- public gravatarEnabled$ = this.store.select(selectDashboardState).pipe(
+ public gravatarEnabled$ = this.dashboardState$.pipe(
map(dashboardState => dashboardState.gravatarEnabled ? 'true' : 'false')
);
- public allowGravatar$ = this.store.select(selectDashboardState).pipe(
+ public allowGravatar$ = this.dashboardState$.pipe(
map(dashboardState => dashboardState.gravatarEnabled)
);
@@ -44,6 +50,8 @@ export class ProfileInfoComponent {
primaryEmailAddress$: Observable;
hasMultipleThemes: boolean;
+ localStorageSize$: Observable;
+
public updateSessionKeepAlive(timeoutSession: string) {
const newVal = !(timeoutSession === 'true');
this.setSessionTimeout(newVal);
@@ -71,10 +79,11 @@ export class ProfileInfoComponent {
}
constructor(
- private userProfileService: UserProfileService,
- private store: Store,
+ userProfileService: UserProfileService,
+ private store: Store,
public userService: UserService,
public themeService: ThemeService,
+ private confirmationService: ConfirmationDialogService,
private currentUserPermissionsService: CurrentUserPermissionsService,
) {
this.isError$ = userProfileService.isError$;
@@ -89,6 +98,15 @@ export class ProfileInfoComponent {
);
this.hasMultipleThemes = themeService.getThemes().length > 1;
+
+ this.localStorageSize$ = this.sessionData$.pipe(
+ map(sessionData => LocalStorageService.localStorageSize(sessionData)),
+ filter(bytes => bytes !== -1),
+ );
+ }
+
+ clearLocalStorage() {
+ this.sessionData$.pipe(first()).subscribe(sessionData => LocalStorageService.clearLocalStorage(sessionData, this.confirmationService));
}
}
diff --git a/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-data-source.ts b/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-data-source.ts
index 16ea225294..78e6054907 100644
--- a/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-data-source.ts
+++ b/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-data-source.ts
@@ -146,6 +146,7 @@ export abstract class ListDataSource extends DataSource implements
this.masterAction,
this.isLocal
);
+
const { pagination$, entities$ } = getPaginationObservables({
store: this.store,
action: this.action,
@@ -266,6 +267,16 @@ export abstract class ListDataSource extends DataSource implements
}
this.entitySelectConfig = this.getEntitySelectConfig(config.schema);
}
+ /* tslint:disable-next-line:no-string-literal */
+ if (this.action['length']) {
+ this.action = (this.action as PaginatedAction[]).map(a => ({
+ ...a,
+ isList: true
+ }));
+ } else {
+ (this.action as PaginatedAction).isList = true;
+ }
+ this.masterAction.isList = true;
}
private getEntitySelectConfig(multiActionConfig: MultiActionConfig) {
diff --git a/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-pagination-controller.ts b/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-pagination-controller.ts
index 0cdf9bd632..05cb64e459 100644
--- a/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-pagination-controller.ts
+++ b/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-pagination-controller.ts
@@ -115,7 +115,7 @@ export class ListPaginationController implements IListPaginationController
}, this.dataSource.isLocal));
}
});
- }
+ };
filterByString = filterString => {
onPaginationEntityState(this.dataSource.pagination$, (paginationEntityState) => {
if (this.dataSource.isLocal) {
@@ -132,7 +132,7 @@ export class ListPaginationController implements IListPaginationController
this.dataSource.setFilterParam(filterString, paginationEntityState);
}
});
- }
+ };
handleMultiFilter = (changes: ListPaginationMultiFilterChange[]) => {
onPaginationEntityState(this.dataSource.pagination$, (paginationEntityState) => {
@@ -140,9 +140,17 @@ export class ListPaginationController implements IListPaginationController
return;
}
- // We don't want to dispatch actions if it's a no op (values are not different, falsies are treated as the same). This avoids other
+ // Changes may include multiple updates for the same key, so only use the very latest
+ const uniqueChanges = [];
+ for (let i = changes.length - 1; i >= 0; i--) {
+ const change = changes[i];
+ if (!uniqueChanges.find(e => e.key === change.key)) {
+ uniqueChanges.push(change);
+ }
+ }
+ // We don't want to dispatch actions if it's a no op (values are not different, falsies are treated as the same). This avoids other
// chained actions from firing.
- const cleanChanges = changes.reduce((newCleanChanges, change) => {
+ const cleanChanges = uniqueChanges.reduce((newCleanChanges, change) => {
const storeFilterParamValue = valueOrCommonFalsy(paginationEntityState.clientPagination.filter.items[change.key]);
const newFilterParamValue = valueOrCommonFalsy(change.value);
if (storeFilterParamValue !== newFilterParamValue) {
@@ -172,14 +180,14 @@ export class ListPaginationController implements IListPaginationController
}
});
- }
+ };
multiFilter = (filterConfig: IListMultiFilterConfig, filterValue: string) => {
if (!this.dataSource.isLocal) {
return;
}
this.multiFilterStream.next({ key: filterConfig.key, value: filterValue });
- }
+ };
private cloneMultiFilter(paginationClientFilter: PaginationClientFilter) {
return {
diff --git a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts
index 59764085a0..24fb90718b 100644
--- a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts
+++ b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts
@@ -153,7 +153,7 @@ export class EndpointsListConfigService implements IListConfig {
stratosEntityCatalog.endpoint.store.getPaginationMonitor().pagination$
]).pipe(
debounceTime(100), // This can get pretty spammy, to help protect resetEndpointTypeFilter allow a pause
- filter(([endpoints, pagination]) => !!endpoints),
+ filter(([endpoints]) => !!endpoints),
map(([endpoints, pagination]) => {
// Provide a list of endpoint types only if there are more than two registered endpoint types
const types: { [type: string]: boolean; } = {};
diff --git a/src/frontend/packages/core/src/shared/components/list/list.component.html b/src/frontend/packages/core/src/shared/components/list/list.component.html
index db67aaa1e0..79b69e69bf 100644
--- a/src/frontend/packages/core/src/shared/components/list/list.component.html
+++ b/src/frontend/packages/core/src/shared/components/list/list.component.html
@@ -64,8 +64,7 @@