diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts index 5c2a6d820e..e41d3a45b2 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts @@ -22,6 +22,7 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { APP_CONFIG } from '../../../config/app-config.interface'; import { environment } from '../../../environments/environment'; +import { SortDirection } from '../../core/cache/models/sort-options.model'; describe('BrowseByDatePageComponent', () => { let comp: BrowseByDatePageComponent; @@ -49,12 +50,22 @@ describe('BrowseByDatePageComponent', () => { ] } }); + const lastItem = Object.assign(new Item(), { + id: 'last-item-id', + metadata: { + 'dc.date.issued': [ + { + value: '1960-01-01' + } + ] + } + }); - const mockBrowseService = { - getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData([]), - getBrowseItemsFor: (value: string, options: BrowseEntrySearchOptions) => toRemoteData([firstItem]), - getFirstItemFor: () => createSuccessfulRemoteDataObject$(firstItem) - }; + const mockBrowseService = { + getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData([]), + getBrowseItemsFor: (value: string, options: BrowseEntrySearchOptions) => toRemoteData([firstItem]), + getFirstItemFor: (definition: string, scope?: string, sortDirection?: SortDirection) => null + }; const mockDsoService = { findById: () => createSuccessfulRemoteDataObject$(mockCommunity) @@ -91,9 +102,14 @@ describe('BrowseByDatePageComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(BrowseByDatePageComponent); + const browseService = fixture.debugElement.injector.get(BrowseService); + spyOn(browseService, 'getFirstItemFor') + // ok to expect the default browse as first param since we just need the mock items obtained via sort direction. + .withArgs('author', undefined, SortDirection.ASC).and.returnValue(createSuccessfulRemoteDataObject$(firstItem)) + .withArgs('author', undefined, SortDirection.DESC).and.returnValue(createSuccessfulRemoteDataObject$(lastItem)); comp = fixture.componentInstance; - fixture.detectChanges(); route = (comp as any).route; + fixture.detectChanges(); }); it('should initialize the list of items', () => { @@ -107,6 +123,7 @@ describe('BrowseByDatePageComponent', () => { }); it('should create a list of startsWith options with the current year first', () => { - expect(comp.startsWithOptions[0]).toEqual(new Date().getUTCFullYear()); + //expect(comp.startsWithOptions[0]).toEqual(new Date().getUTCFullYear()); + expect(comp.startsWithOptions[0]).toEqual(1960); }); }); diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index ffa7e882af..01cd3a003e 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -1,11 +1,10 @@ import { ChangeDetectorRef, Component, Inject } from '@angular/core'; import { BrowseByMetadataPageComponent, - browseParamsToOptions, getBrowseSearchOptions + browseParamsToOptions, + getBrowseSearchOptions } from '../browse-by-metadata-page/browse-by-metadata-page.component'; import { combineLatest as observableCombineLatest } from 'rxjs'; -import { RemoteData } from '../../core/data/remote-data'; -import { Item } from '../../core/shared/item.model'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { BrowseService } from '../../core/browse/browse.service'; @@ -16,7 +15,9 @@ import { map } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { isValidDate } from '../../shared/date.util'; -import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface'; +import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; +import { RemoteData } from '../../core/data/remote-data'; +import { Item } from '../../core/shared/item.model'; @Component({ selector: 'ds-browse-by-date-page', @@ -72,30 +73,24 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { /** * Update the StartsWith options - * In this implementation, it creates a list of years starting from now, going all the way back to the earliest - * date found on an item within this scope. The further back in time, the bigger the change in years become to avoid - * extremely long lists with a one-year difference. + * In this implementation, it creates a list of years starting from the most recent item or the current year, going + * all the way back to the earliest date found on an item within this scope. The further back in time, the bigger + * the change in years become to avoid extremely long lists with a one-year difference. * To determine the change in years, the config found under GlobalConfig.BrowseBy is used for this. * @param definition The metadata definition to fetch the first item for * @param metadataKeys The metadata fields to fetch the earliest date from (expects a date field) * @param scope The scope under which to fetch the earliest item for */ updateStartsWithOptions(definition: string, metadataKeys: string[], scope?: string) { + const firstItemRD = this.browseService.getFirstItemFor(definition, scope, SortDirection.ASC); + const lastItemRD = this.browseService.getFirstItemFor(definition, scope, SortDirection.DESC); this.subs.push( - this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData) => { - let lowerLimit = this.appConfig.browseBy.defaultLowerLimit; - if (hasValue(firstItemRD.payload)) { - const date = firstItemRD.payload.firstMetadataValue(metadataKeys); - if (isNotEmpty(date) && isValidDate(date)) { - const dateObj = new Date(date); - // TODO: it appears that getFullYear (based on local time) is sometimes unreliable. Switching to UTC. - lowerLimit = isNaN(dateObj.getUTCFullYear()) ? lowerLimit : dateObj.getUTCFullYear(); - } - } + observableCombineLatest([firstItemRD, lastItemRD]).subscribe(([firstItem, lastItem]) => { + let lowerLimit = this.getLimit(firstItem, metadataKeys, this.appConfig.browseBy.defaultLowerLimit); + let upperLimit = this.getLimit(lastItem, metadataKeys, new Date().getUTCFullYear()); const options = []; - const currentYear = new Date().getUTCFullYear(); - const oneYearBreak = Math.floor((currentYear - this.appConfig.browseBy.oneYearLimit) / 5) * 5; - const fiveYearBreak = Math.floor((currentYear - this.appConfig.browseBy.fiveYearLimit) / 10) * 10; + const oneYearBreak = Math.floor((upperLimit - this.appConfig.browseBy.oneYearLimit) / 5) * 5; + const fiveYearBreak = Math.floor((upperLimit - this.appConfig.browseBy.fiveYearLimit) / 10) * 10; if (lowerLimit <= fiveYearBreak) { lowerLimit -= 10; } else if (lowerLimit <= oneYearBreak) { @@ -103,7 +98,7 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { } else { lowerLimit -= 1; } - let i = currentYear; + let i = upperLimit; while (i > lowerLimit) { options.push(i); if (i <= fiveYearBreak) { @@ -121,4 +116,24 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { }) ); } + + /** + * Returns the year from the item metadata field or the limit. + * @param itemRD the item remote data + * @param metadataKeys The metadata fields to fetch the earliest date from (expects a date field) + * @param limit the limit to use if the year can't be found in metadata + * @private + */ + private getLimit(itemRD: RemoteData, metadataKeys: string[], limit: number): number { + if (hasValue(itemRD.payload)) { + const date = itemRD.payload.firstMetadataValue(metadataKeys); + if (isNotEmpty(date) && isValidDate(date)) { + const dateObj = new Date(date); + // TODO: it appears that getFullYear (based on local time) is sometimes unreliable. Switching to UTC. + return isNaN(dateObj.getUTCFullYear()) ? limit : dateObj.getUTCFullYear(); + } else { + return new Date().getUTCFullYear(); + } + } + } } diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index be28015069..989213a978 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -22,6 +22,7 @@ import { BrowseEntrySearchOptions } from './browse-entry-search-options.model'; import { HrefOnlyDataService } from '../data/href-only-data.service'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { BrowseDefinitionDataService } from './browse-definition-data.service'; +import { SortDirection } from '../cache/models/sort-options.model'; export const BROWSE_LINKS_TO_FOLLOW: FollowLinkConfig[] = [ @@ -160,8 +161,9 @@ export class BrowseService { * Get the first item for a metadata definition in an optional scope * @param definition * @param scope + * @param sortDirection optional sort parameter */ - getFirstItemFor(definition: string, scope?: string): Observable> { + getFirstItemFor(definition: string, scope?: string, sortDirection?: SortDirection): Observable> { const href$ = this.getBrowseDefinitions().pipe( getBrowseDefinitionLinks(definition), hasValueOperator(), @@ -177,6 +179,9 @@ export class BrowseService { } args.push('page=0'); args.push('size=1'); + if (sortDirection) { + args.push('sort=default,' + sortDirection); + } if (isNotEmpty(args)) { href = new URLCombiner(href, `?${args.join('&')}`).toString(); }