Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/app/core/services/route.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,34 @@ export class RouteService {
});
}

/**
* Store a URL in session storage for later retrieval
* Generic method that can be used by any component
* @param key The session storage key
* @param url The URL to store
*/
public storeUrlInSession(key: string, url: string): void {
if (typeof window !== 'undefined' && hasValue(window.sessionStorage)) {
// Only write if the value is different to avoid unnecessary writes
const currentValue = window.sessionStorage.getItem(key);
if (currentValue !== url) {
window.sessionStorage.setItem(key, url);
}
}
}

/**
* Retrieve a URL from session storage
* Generic method that can be used by any component
* @param key The session storage key
*/
public getUrlFromSession(key: string): string | null {
if (typeof window !== 'undefined' && hasValue(window.sessionStorage)) {
return window.sessionStorage.getItem(key);
}
return null;
}

private getRouteParams(): Observable<Params> {
let active = this.route;
while (active.firstChild) {
Expand Down
10 changes: 10 additions & 0 deletions src/app/core/testing/route-service.stub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ export const routeServiceStub: any = {
getPreviousUrl: () => {
return of('/home');
},
// Added generic session helpers used by ItemComponent
storeUrlInSession: (key: string, url: string) => {
// no-op for tests
},
getUrlFromSession: (key: string): string | null => {
return null;
},
clearUrlFromSession: (key: string) => {
// no-op for tests
},
setParameter: (key: any, value: any) => {
return;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ describe('PublicationComponent', () => {
getPreviousUrl(): Observable<string> {
return of('/search?query=test%20query&fakeParam=true');
},
storeUrlInSession(key: string, url: string): void {
// no-op
},
getUrlFromSession(key: string): string | null {
return null;
},
};
beforeEach(waitForAsync(() => {
const iiifEnabledMap: MetadataMap = {
Expand Down Expand Up @@ -243,6 +249,12 @@ describe('PublicationComponent', () => {
getPreviousUrl(): Observable<string> {
return of('/item');
},
storeUrlInSession(key: string, url: string): void {
// no-op
},
getUrlFromSession(key: string): string | null {
return null;
},
};
beforeEach(waitForAsync(() => {
const iiifEnabledMap: MetadataMap = {
Expand Down
33 changes: 33 additions & 0 deletions src/app/item-page/simple/item-types/shared/item.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ export const mockRouteService = {
getPreviousUrl(): Observable<string> {
return of('');
},
storeUrlInSession(key: string, url: string): void {
// no-op
},
getUrlFromSession(key: string): string | null {
return null;
},
getQueryParameterValue(): Observable<string> {
return of('');
},
Expand Down Expand Up @@ -483,6 +489,7 @@ describe('ItemComponent', () => {

const searchUrl = '/search?query=test&spc.page=2';
const browseUrl = '/browse/title?scope=0cc&bbm.page=3';
const homeUrl = '/home';
const recentSubmissionsUrl = '/collections/be7b8430-77a5-4016-91c9-90863e50583a?cp.page=3';

beforeEach(waitForAsync(() => {
Expand Down Expand Up @@ -564,6 +571,32 @@ describe('ItemComponent', () => {
expect(val).toBeTrue();
});
});

it('should show back button for home', () => {
spyOn(mockRouteService, 'getPreviousUrl').and.returnValue(of(homeUrl));
comp.ngOnInit();
comp.showBackButton$.subscribe((val) => {
expect(val).toBeTrue();
});
});

it('should prioritize home previous url over session fallback', () => {
const staleSessionUrl = searchUrl;
const getPreviousUrlSpy = spyOn(mockRouteService, 'getPreviousUrl').and.returnValue(of(homeUrl));
const getUrlFromSessionSpy = spyOn(mockRouteService, 'getUrlFromSession').and.returnValue(staleSessionUrl);
const storeUrlInSessionSpy = spyOn(mockRouteService, 'storeUrlInSession');

comp.ngOnInit();
comp.showBackButton$.subscribe((val) => {
expect(val).toBeTrue();
expect(getPreviousUrlSpy).toHaveBeenCalled();
expect(getUrlFromSessionSpy).not.toHaveBeenCalled();
expect(storeUrlInSessionSpy).toHaveBeenCalledWith('item-previous-url', homeUrl);

comp.back();
expect(router.navigateByUrl).toHaveBeenCalledWith(homeUrl);
});
});
});

});
47 changes: 37 additions & 10 deletions src/app/item-page/simple/item-types/shared/item.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ export class ItemComponent implements OnInit {
*/
@Input() viewMode: ViewMode;

/**
* Session storage key for storing the previous URL before entering item page
*/
private readonly ITEM_PREVIOUS_URL_SESSION_KEY = 'item-previous-url';

/**
* This regex matches previous routes. The button is shown
* for matching paths and hidden in other cases.
*/
previousRoute = /^(\/search|\/browse|\/collections|\/admin\/search|\/mydspace)/;
previousRoute = /^(\/home|\/search|\/browse|\/collections|\/admin\/search|\/mydspace)/;

/**
* Used to show or hide the back to results button in the view.
Expand Down Expand Up @@ -79,6 +84,11 @@ export class ItemComponent implements OnInit {
*/
geospatialItemPageFieldsEnabled = false;

/**
* Stores the previous URL retrieved either from RouteService or sessionStorage
*/
private storedPreviousUrl: string;

constructor(protected routeService: RouteService,
protected router: Router) {
this.mediaViewer = environment.mediaViewer;
Expand All @@ -87,24 +97,34 @@ export class ItemComponent implements OnInit {

/**
* The function used to return to list from the item.
* Uses stored previous URL if available, otherwise falls back to browser history.
*/
back = () => {
this.routeService.getPreviousUrl().pipe(
take(1),
).subscribe(
(url => {
this.router.navigateByUrl(url);
}),
);
this.router.navigateByUrl(this.storedPreviousUrl);
};

ngOnInit(): void {

this.itemPageRoute = getItemPageRoute(this.object);
// hide/show the back button
this.showBackButton$ = this.routeService.getPreviousUrl().pipe(
map((url: string) => this.previousRoute.test(url)),
take(1),
map(url => {
const fromRoute = this.pickAllowedPrevious(url);

if (fromRoute) {
this.routeService.storeUrlInSession(this.ITEM_PREVIOUS_URL_SESSION_KEY, fromRoute);
this.storedPreviousUrl = fromRoute;
return true;
}

const storedUrl = this.routeService.getUrlFromSession(this.ITEM_PREVIOUS_URL_SESSION_KEY);
if (this.pickAllowedPrevious(storedUrl)) {
this.storedPreviousUrl = storedUrl;
return true;
}

return false;
}),
);
// check to see if iiif viewer is required.
this.iiifEnabled = isIiifEnabled(this.object);
Expand All @@ -113,4 +133,11 @@ export class ItemComponent implements OnInit {
this.iiifQuery$ = getDSpaceQuery(this.object, this.routeService);
}
}

/**
* Helper to check if a URL is from an allowed previous route and return it, otherwise null
*/
private pickAllowedPrevious(url?: string | null): string | null {
return url && this.previousRoute.test(url) ? url : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ describe('UntypedItemComponent', () => {
getPreviousUrl(): Observable<string> {
return of('/search?query=test%20query&fakeParam=true');
},
storeUrlInSession(key: string, url: string): void {
// no-op
},
getUrlFromSession(key: string): string | null {
return null;
},
};
beforeEach(waitForAsync(() => {
const iiifEnabledMap: MetadataMap = {
Expand Down Expand Up @@ -266,6 +272,12 @@ describe('UntypedItemComponent', () => {
getPreviousUrl(): Observable<string> {
return of('/item');
},
storeUrlInSession(key: string, url: string): void {
// no-op
},
getUrlFromSession(key: string): string | null {
return null;
},
};
beforeEach(waitForAsync(() => {
const iiifEnabledMap: MetadataMap = {
Expand Down
Loading