Skip to content

Commit f02291a

Browse files
Removed dupe pageview. Resolves #1198. (#1211)
1 parent d61b2e3 commit f02291a

20 files changed

+270
-82
lines changed

spa/src/app/_ngrx/app.effects.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ReleaseEffects } from "../files/_ngrx/release/release.effects";
1717
import { SystemEffects } from "../system/_ngrx/system.effects";
1818
import { TableEffects } from "../files/_ngrx/table/table.effects";
1919
import { TerraEffects } from "../files/_ngrx/terra/terra.effects";
20+
import { UrlEffects } from "../files/_ngrx/url/url.effects";
2021

2122
export const AppEffects = [
2223
ConfigEffects,
@@ -29,5 +30,6 @@ export const AppEffects = [
2930
ReleaseEffects,
3031
SystemEffects,
3132
TableEffects,
32-
TerraEffects
33+
TerraEffects,
34+
UrlEffects
3335
];

spa/src/app/app.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,12 @@ export class AppComponent implements OnInit, OnDestroy {
218218

219219
const filter = this.searchUrlService.parseQueryStringSearchTerms(params);
220220

221-
// Default app state to have human selected. This is only necessary if there is currently no filter
221+
// Default app state is to have human selected. This is only necessary if there is currently no filter
222222
// applied.
223223
if ( filter.length === 0 ) {
224224
filter.push(this.searchUrlService.getDefaultSearchState());
225225
}
226-
226+
227227
this.store.dispatch(new SetViewStateAction(tab, filter));
228228

229229
if ( this.routerEventsSubscription ) {

spa/src/app/app.routes.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,25 @@
99
import { Route } from "@angular/router";
1010

1111
// App components
12+
import { BrowserCanActivateGuard } from "./shared/routing/browser.can-activate.guard";
1213
import { ErrorComponent } from "./system/error/error.component";
1314
import { NotFoundComponent } from "./system/not-found/not-found.component";
14-
import { BrowserCanActivateGuard } from "./shared/routing/browser.can-activate.guard";
1515

1616
export const AppRoutes: Route[] = [
1717
{
1818
path: "",
19-
canActivate: [BrowserCanActivateGuard],
2019
children: [
2120
{
2221
path: "",
2322
redirectTo: "/projects",
2423
pathMatch: "full"
2524
}, {
2625
path: "error",
26+
canActivate: [BrowserCanActivateGuard],
2727
component: ErrorComponent
2828
}, {
2929
path: "not-found",
30+
canActivate: [BrowserCanActivateGuard],
3031
component: NotFoundComponent
3132
},
3233
{

spa/src/app/files/_ngrx/file.effects.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export class FileEffects {
6969
ofType(
7070
ClearSelectedTermsAction.ACTION_TYPE, // Clear all selected terms
7171
ClearSelectedAgeRangeAction.ACTION_TYPE, // Clear age range
72-
InitEntityStateAction.ACTION_TYPE,
72+
InitEntityStateAction.ACTION_TYPE, // Init table data for newly selected tab, if table data isn't cached
7373
SetViewStateAction.ACTION_TYPE, // Setting up app state from URL params
7474
SelectFileFacetTermAction.ACTION_TYPE, // Selecting facet term eg file type "matrix"
7575
SelectFacetAgeRangeAction.ACTION_TYPE // Setting age range

spa/src/app/files/_ngrx/file.selectors.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { createFeatureSelector, createSelector } from "@ngrx/store";
1111
// App dependencies
1212
import { FileSummaryState } from "./file-summary/file-summary.state";
1313
import { selectSelectedSearchTermsBySearchKey } from "./search/search.selectors";
14-
import { getSelectedEntity, getSelectedTable, TableState } from "./table/table.state";
14+
import { getSelectedEntitySpec, getSelectedTable, TableState } from "./table/table.state";
1515
import { Pagination } from "../table/pagination/pagination.model";
1616

1717
/**
@@ -80,8 +80,8 @@ export const selectEntities = createSelector(selectTableState, (tableState: Tabl
8080
*
8181
* @returns {EntitySpec}
8282
*/
83-
export const selectSelectedEntity = createSelector(selectTableState, (tableState: TableState) => {
84-
return getSelectedEntity(tableState);
83+
export const selectSelectedEntitySpec = createSelector(selectTableState, (tableState: TableState) => {
84+
return getSelectedEntitySpec(tableState);
8585
});
8686

8787
/**

spa/src/app/files/_ngrx/table/table.state.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export function getSelectedTable(tableState: TableState): TableModel {
9090
* @param {TableState} tableState
9191
* @returns {EntitySpec}
9292
*/
93-
export function getSelectedEntity(tableState: TableState): EntitySpec {
93+
export function getSelectedEntitySpec(tableState: TableState): EntitySpec {
9494
return tableState.entitySpecs.find(entity => entity.key === tableState.selectedEntity);
9595
}
9696

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* Human Cell Atlas
3+
* https://www.humancellatlas.org/
4+
*
5+
* URL-related side effects.
6+
*/
7+
8+
// Core dependencies
9+
import { Injectable } from "@angular/core";
10+
import { Location } from "@angular/common";
11+
import { Actions, Effect, ofType } from "@ngrx/effects";
12+
import { select, Store } from "@ngrx/store";
13+
import { filter, map, switchMap, take, tap } from "rxjs/operators";
14+
15+
// App dependencies
16+
import { AppState } from "../../../_ngrx/app.state";
17+
import { ClearSelectedAgeRangeAction } from "../search/clear-selected-age-range.action";
18+
import { SelectFacetAgeRangeAction } from "../search/select-facet-age-range.action";
19+
import { ClearSelectedTermsAction } from "../search/clear-selected-terms.action";
20+
import { SelectFileFacetTermAction } from "../search/select-file-facet-term.action";
21+
import { selectUrlSpecState } from "./url.selectors";
22+
import { ActivatedRoute, Router } from "@angular/router";
23+
import { EntityName } from "../../shared/entity-name.model";
24+
import { SearchTermUrlService } from "../../search/url/search-term-url.service";
25+
import { EntitySelectAction } from "../table/table.actions";
26+
import { SetViewStateAction } from "../facet/set-view-state.action";
27+
28+
@Injectable()
29+
export class UrlEffects {
30+
31+
/**
32+
* @param {SearchTermUrlService} searchTermUrlService
33+
* @param {Store<AppState>} store
34+
* @param {Location} location
35+
* @param {Router} router
36+
* @param {Actions} actions$
37+
*/
38+
constructor(private searchTermUrlService: SearchTermUrlService,
39+
private store: Store<AppState>,
40+
private location: Location,
41+
private router: Router,
42+
private actions$: Actions) {
43+
}
44+
45+
/**
46+
* Update filter query string param if selected entity or selected search terms has changed.
47+
*/
48+
@Effect({dispatch: false})
49+
updateFilterQueryParam$ = this.actions$.pipe(
50+
ofType(
51+
ClearSelectedTermsAction.ACTION_TYPE,
52+
ClearSelectedAgeRangeAction.ACTION_TYPE,
53+
EntitySelectAction.ACTION_TYPE,
54+
SelectFileFacetTermAction.ACTION_TYPE,
55+
SelectFacetAgeRangeAction.ACTION_TYPE,
56+
SetViewStateAction.ACTION_TYPE
57+
),
58+
filter(() => {
59+
return this.router.isActive(EntityName.PROJECTS, false) ||
60+
this.router.isActive(EntityName.SAMPLES, false) ||
61+
this.router.isActive(EntityName.FILES, false)
62+
}),
63+
switchMap(() =>
64+
this.store.pipe(
65+
select(selectUrlSpecState),
66+
take(1)
67+
)
68+
),
69+
tap((urlSpecState) => {
70+
71+
const filterQueryString =
72+
this.searchTermUrlService.stringifySearchTerms(urlSpecState.selectedSearchTermsBySearchKey);
73+
74+
const path = urlSpecState.selectedEntitySpec.key;
75+
const params = new URLSearchParams();
76+
if ( !!filterQueryString ) {
77+
params.set("filter", filterQueryString);
78+
}
79+
this.location.replaceState(path, params.toString());
80+
81+
})
82+
);
83+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Human Cell Atlas
3+
* https://www.humancellatlas.org/
4+
*
5+
* Selectors for querying state required for URL-related functionality, from the store.
6+
*/
7+
8+
// Core dependencies
9+
import { createSelector } from "@ngrx/store";
10+
11+
// App dependencies
12+
import { selectSelectedEntitySpec } from "../file.selectors";
13+
import { selectSelectedSearchTermsBySearchKey } from "../search/search.selectors";
14+
15+
/**
16+
* Return the selected entity spec (required for path-related functionality) and selected search terms (required
17+
* for filter query string param functionality), combined into a single object.
18+
*/
19+
export const selectUrlSpecState = createSelector(
20+
selectSelectedEntitySpec, selectSelectedSearchTermsBySearchKey,
21+
(selectedEntitySpec, selectedSearchTermsBySearchKey) => {
22+
return {selectedEntitySpec, selectedSearchTermsBySearchKey};
23+
});

spa/src/app/files/files.component.ts

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@
77

88
// Core dependencies
99
import { Component, OnDestroy, OnInit, Renderer2 } from "@angular/core";
10-
import { Location } from "@angular/common";
11-
import * as _ from "lodash";
1210
import { DeviceDetectorService } from "ngx-device-detector";
1311
import { select, Store } from "@ngrx/store";
1412
import { combineLatest, Observable, Subject } from "rxjs";
15-
import { distinctUntilChanged, map, takeUntil } from "rxjs/operators";
13+
import { map, takeUntil } from "rxjs/operators";
1614

1715
// App dependencies
1816
import { FilesState } from "./files.state";
@@ -21,16 +19,15 @@ import { selectFacetFacets } from "./_ngrx/facet/facet.selectors";
2119
import {
2220
selectFileSummary,
2321
selectEntities,
24-
selectSelectedEntity
22+
selectSelectedEntitySpec
2523
} from "./_ngrx/file.selectors";
2624
import {
2725
selectSearchTerms,
2826
selectSelectedProjectSearchTerms,
29-
selectSelectedSearchTerms, selectSelectedSearchTermsBySearchKey
27+
selectSelectedSearchTerms
3028
} from "./_ngrx/search/search.selectors";
3129
import { EntitySelectAction } from "./_ngrx/table/table.actions";
3230
import { SearchTerm } from "./search/search-term.model";
33-
import { SearchTermUrlService } from "./search/url/search-term-url.service";
3431
import EntitySpec from "./shared/entity-spec";
3532

3633
@Component({
@@ -48,14 +45,10 @@ export class FilesComponent implements OnInit, OnDestroy {
4845

4946
/**
5047
* @param {DeviceDetectorService} deviceService
51-
* @param {SearchTermUrlService} locationService
52-
* @param {Location} location
5348
* @param {Store<AppState>} store
5449
* @param {Renderer2} renderer
5550
*/
5651
constructor(private deviceService: DeviceDetectorService,
57-
private locationService: SearchTermUrlService,
58-
private location: Location,
5952
private store: Store<AppState>,
6053
private renderer: Renderer2) {
6154
}
@@ -99,35 +92,6 @@ export class FilesComponent implements OnInit, OnDestroy {
9992
this.store.dispatch(new EntitySelectAction(tab.key));
10093
}
10194

102-
/**
103-
* Update URL on change of selected facets.
104-
*/
105-
private initOnStateChanges() {
106-
107-
// Set up the URL state management - write to the browser address bar when the selected facets change.
108-
combineLatest(
109-
this.store.pipe(select(selectSelectedEntity)),
110-
this.store.pipe(select(selectSelectedSearchTermsBySearchKey))
111-
)
112-
.pipe(
113-
takeUntil(this.ngDestroy$),
114-
distinctUntilChanged((previous, current) => {
115-
return _.isEqual(previous, current);
116-
})
117-
)
118-
.subscribe(([selectedEntity, selectedSearchTermsBySearchKey]) => {
119-
120-
const filterQueryString = this.locationService.stringifySearchTerms(selectedSearchTermsBySearchKey);
121-
122-
const path = selectedEntity.key;
123-
const params = new URLSearchParams();
124-
if ( !!filterQueryString ) {
125-
params.set("filter", filterQueryString);
126-
}
127-
this.location.replaceState(path, params.toString());
128-
});
129-
}
130-
13195
/**
13296
* Setup initial component state from store.
13397
*/
@@ -137,7 +101,7 @@ export class FilesComponent implements OnInit, OnDestroy {
137101
this.store.pipe(select(selectFileSummary)), // Counts
138102
this.store.pipe(select(selectFacetFacets)), // Complete list of facets to display - both file facets (facets with term lists) as well as range facets
139103
this.store.pipe(select(selectEntities)), // Set of tabs to be displayed
140-
this.store.pipe(select(selectSelectedEntity)), // Current selected tab
104+
this.store.pipe(select(selectSelectedEntitySpec)), // Current selected tab
141105
this.store.pipe(select(selectSelectedSearchTerms)), // Set of possible search terms, used to populate the search autosuggest.
142106
this.store.pipe(select(selectSearchTerms)),
143107
this.store.pipe( // Current set of selected projects, if any
@@ -196,6 +160,5 @@ export class FilesComponent implements OnInit, OnDestroy {
196160
public ngOnInit() {
197161

198162
this.initState();
199-
this.initOnStateChanges();
200163
}
201164
}

spa/src/app/files/files.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ import { ProjectMetadataComponent } from "./project-metadata/project-metadata.co
100100
import { ProjectNavComponent } from "./project-nav/project-nav.component";
101101
import { ProjectOverviewDataSummaryComponent } from "./project-overview-data-summary/project-overview-data-summary.component";
102102
import { ProjectOverviewComponent } from "./project-overview/project-overview.component";
103+
import { ProjectsCanActivateGuard } from "./project/projects.can-activate.guard";
103104
import { ProjectService } from "./project/project.service";
104105
import { ProjectReleaseComponent } from "./project-release/project-release.component";
105106
import { ProjectSummaryStatsComponent } from "./project-summary-stats-component/project-summary-stats.component";
@@ -268,6 +269,7 @@ const v2 = environment.version === "2.0";
268269
ProjectAnalyticsService,
269270
ProjectService,
270271
ProjectEditsService,
272+
ProjectsCanActivateGuard,
271273
ProjectViewFactory,
272274
ReleaseService,
273275
SearchTermHttpService,

0 commit comments

Comments
 (0)