diff --git a/src/app/+search-page/search-filters/search-filter/search-filter-type-decorator.ts b/src/app/+search-page/search-filters/search-filter/search-filter-type-decorator.ts index 24a64698ad..bcc22be82c 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter-type-decorator.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter-type-decorator.ts @@ -1,12 +1,15 @@ import { FilterType } from '../../search-service/filter-type.model'; +/** + * Contains the mapping between a facet component and a FilterType + */ const filterTypeMap = new Map(); /** - * Sets the mapping for a component in relation to a filter type + * Sets the mapping for a facet component in relation to a filter type * @param {FilterType} type The type for which the matching component is mapped - * @returns Decorator function that performs the actual mapping on initialization of the component + * @returns Decorator function that performs the actual mapping on initialization of the facet component */ export function renderFacetFor(type: FilterType) { return function decorator(objectElement: any) { @@ -18,9 +21,9 @@ export function renderFacetFor(type: FilterType) { } /** - * Requests the matching component based on a given filter type - * @param {FilterType} type The filter type for which the component is requested - * @returns The component's constructor that matches the given filter type + * Requests the matching facet component based on a given filter type + * @param {FilterType} type The filter type for which the facet component is requested + * @returns The facet component's constructor that matches the given filter type */ export function renderFilterType(type: FilterType) { return filterTypeMap.get(type); diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index 238045aaf8..6cb781bedf 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -26,6 +26,9 @@ const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; export const FILTER_CONFIG: InjectionToken = new InjectionToken('filterConfig'); +/** + * Service that performs all actions that have to do with search filters and facets + */ @Injectable() export class SearchFilterService { diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 4fb5020a17..1cd6477e2a 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -35,13 +35,30 @@ import { hasValue } from '../shared/empty.util'; */ export class SearchPageComponent implements OnInit { + /** + * The current search results + */ resultsRD$: Subject>>> = new Subject(); + + /** + * The current paginated search options + */ searchOptions$: Observable; sortConfig: SortOptions; + + /** + * The current relevant scopes + */ scopeListRD$: Observable; + + /** + * Emits true if were on a small screen + */ isXsOrSm$: Observable; - pageSize; - pageSizeOptions; + + /** + * Default values for the Search Options + */ defaults = { pagination: { id: 'search-results-pagination', @@ -51,6 +68,10 @@ export class SearchPageComponent implements OnInit { query: '', scope: '' }; + + /** + * Subscription to unsubscribe from + */ sub: Subscription; constructor(private service: SearchService, @@ -93,7 +114,7 @@ export class SearchPageComponent implements OnInit { } /** - * Check if the sidebar is correct + * Check if the sidebar is collapsed * @returns {Observable} emits true if the sidebar is currently collapsed, false if it is expanded */ public isSidebarCollapsed(): Observable { @@ -107,6 +128,9 @@ export class SearchPageComponent implements OnInit { return this.service.getSearchLink(); } + /** + * Unsubscribe from the subscription + */ ngOnDestroy(): void { if (hasValue(this.sub)) { this.sub.unsubscribe(); diff --git a/src/app/+search-page/search-service/search-filter-config.model.ts b/src/app/+search-page/search-service/search-filter-config.model.ts index edc461c463..dc6fef1f87 100644 --- a/src/app/+search-page/search-service/search-filter-config.model.ts +++ b/src/app/+search-page/search-service/search-filter-config.model.ts @@ -2,19 +2,31 @@ import { autoserialize, autoserializeAs } from 'cerialize'; /** - * + * The configuration for a search filter */ export class SearchFilterConfig { + /** + * The name of this filter + */ @autoserialize name: string; + /** + * The FilterType of this filter + */ @autoserializeAs(String, 'facetType') type: FilterType; + /** + * True if the filter has facets + */ @autoserialize hasFacets: boolean; + /** + * @type {number} The page size used for this facet + */ @autoserializeAs(String, 'facetLimit') pageSize = 5; @@ -35,6 +47,7 @@ */ @autoserialize minValue: string; + /** * Name of this configuration that can be used in a url * @returns Parameter name diff --git a/src/app/+search-page/search-service/search-query-response.model.ts b/src/app/+search-page/search-service/search-query-response.model.ts index b8948d963f..ac1d8b7df3 100644 --- a/src/app/+search-page/search-service/search-query-response.model.ts +++ b/src/app/+search-page/search-service/search-query-response.model.ts @@ -2,46 +2,88 @@ import { autoserialize, autoserializeAs } from 'cerialize'; import { PageInfo } from '../../core/shared/page-info.model'; import { NormalizedSearchResult } from '../normalized-search-result.model'; +/** + * Class representing the response returned by the server when performing a search request + */ export class SearchQueryResponse { + /** + * The scope used in the search request represented by the UUID of a DSpaceObject + */ @autoserialize scope: string; + /** + * The search query used in the search request + */ @autoserialize query: string; + /** + * The currently active filters used in the search request + */ @autoserialize appliedFilters: any[]; // TODO + /** + * The sort parameters used in the search request + */ @autoserialize sort: any; // TODO + /** + * The sort parameters used in the search request + */ @autoserialize configurationName: string; + /** + * The sort parameters used in the search request + */ @autoserialize public type: string; + /** + * Pagination configuration for this response + */ @autoserialize page: PageInfo; + /** + * The results for this query + */ @autoserializeAs(NormalizedSearchResult) objects: NormalizedSearchResult[]; @autoserialize facets: any; // TODO + /** + * The REST url to retrieve the current response + */ @autoserialize self: string; + /** + * The REST url to retrieve the next response + */ @autoserialize next: string; + /** + * The REST url to retrieve the previous response + */ @autoserialize previous: string; + /** + * The REST url to retrieve the first response + */ @autoserialize first: string; + /** + * The REST url to retrieve the last response + */ @autoserialize last: string; } diff --git a/src/app/+search-page/search-service/search-result-element-decorator.ts b/src/app/+search-page/search-service/search-result-element-decorator.ts index 545d1b20eb..348cf7f592 100644 --- a/src/app/+search-page/search-service/search-result-element-decorator.ts +++ b/src/app/+search-page/search-service/search-result-element-decorator.ts @@ -1,8 +1,16 @@ import { GenericConstructor } from '../../core/shared/generic-constructor'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; +/** + * Contains the mapping between a search result component and a DSpaceObject + */ const searchResultMap = new Map(); +/** + * Used to map Search Result components to their matching DSpaceObject + * @param {GenericConstructor} domainConstructor The constructor of the DSpaceObject + * @returns Decorator function that performs the actual mapping on initialization of the component + */ export function searchResultFor(domainConstructor: GenericConstructor) { return function decorator(searchResult: any) { if (!searchResult) { @@ -12,6 +20,11 @@ export function searchResultFor(domainConstructor: GenericConstructor} domainConstructor The DSpaceObject's constructor for which the search result component is requested + * @returns The component's constructor that matches the given DSpaceObject + */ export function getSearchResultFor(domainConstructor: GenericConstructor) { return searchResultMap.get(domainConstructor); } diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index de1197e40c..3fb809ca90 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -45,16 +45,27 @@ import { CommunityDataService } from '../../core/data/community-data.service'; import { CollectionDataService } from '../../core/data/collection-data.service'; import { Collection } from '../../core/shared/collection.model'; + +/** + * Service that performs all general actions that have to do with the search page + */ @Injectable() export class SearchService implements OnDestroy { + /** + * Endpoint link path for retrieving general search results + */ private searchLinkPath = 'discover/search/objects'; - private facetValueLinkPathPrefix = 'discover/facets/'; - private facetConfigLinkPath = 'discover/facets'; + /** + * Endpoint link path for retrieving facet config incl values + */ + private facetLinkPathPrefix = 'discover/facets/'; + + /** + * Subscription to unsubscribe from + */ private sub; - searchOptions: SearchOptions; - constructor(private router: Router, private route: ActivatedRoute, protected responseCache: ResponseCacheService, @@ -63,14 +74,13 @@ export class SearchService implements OnDestroy { private halService: HALEndpointService, private communityService: CommunityDataService, private collectionService: CollectionDataService) { - const pagination: PaginationComponentOptions = new PaginationComponentOptions(); - pagination.id = 'search-results-pagination'; - pagination.currentPage = 1; - pagination.pageSize = 10; - const sort: SortOptions = new SortOptions('score', SortDirection.DESC); - this.searchOptions = Object.assign(new SearchOptions(), { pagination: pagination, sort: sort }); } + /** + * Method to retrieve a paginated list of search results from the server + * @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search + * @returns {Observable>>>} Emits a paginated list with all search results found + */ search(searchOptions?: PaginatedSearchOptions): Observable>>> { const requestObs = this.halService.getEndpoint(this.searchLinkPath).pipe( map((url: string) => { @@ -139,8 +149,13 @@ export class SearchService implements OnDestroy { return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs); } + /** + * Request the filter configuration for a given scope or the whole repository + * @param {string} scope UUID of the object for which config the filter config is requested, when no scope is provided the configuration for the whole repository is loaded + * @returns {Observable>} The found filter configuration + */ getConfig(scope?: string): Observable> { - const requestObs = this.halService.getEndpoint(this.facetConfigLinkPath).pipe( + const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix).pipe( map((url: string) => { const args: string[] = []; @@ -180,12 +195,19 @@ export class SearchService implements OnDestroy { return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, facetConfigObs); } + /** + * Method to request a single page of filter values for a given value + * @param {SearchFilterConfig} filterConfig The filter config for which we want to request filter values + * @param {number} valuePage The page number of the filter values + * @param {SearchOptions} searchOptions The search configuration for the current search + * @param {string} filterQuery The optional query used to filter out filter values + * @returns {Observable>>} Emits the given page of facet values + */ getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string): Observable>> { - const requestObs = this.halService.getEndpoint(this.facetValueLinkPathPrefix + filterConfig.name).pipe( + const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix + filterConfig.name).pipe( map((url: string) => { const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`]; if (hasValue(filterQuery)) { - // args.push(`${filterConfig.paramName}=${filterQuery},query`); args.push(`prefix=${filterQuery}`); } if (hasValue(searchOptions)) { @@ -228,7 +250,12 @@ export class SearchService implements OnDestroy { return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs); } - getScopes(scopeId: string): Observable { + /** + * Request a list of DSpaceObjects that can be used as a scope, based on the current scope + * @param {string} scopeId UUID of the current scope, if the scope is empty, the repository wide scopes will be returned + * @returns {Observable} Emits a list of DSpaceObjects which represent possible scopes + */ + getScopes(scopeId?: string): Observable { if (hasNoValue(scopeId)) { const top: Observable = this.communityService.findTop({ elementsPerPage: 9999 }).pipe( @@ -260,6 +287,10 @@ export class SearchService implements OnDestroy { } + /** + * Requests the current view mode based on the current URL + * @returns {Observable} The current view mode + */ getViewMode(): Observable { return this.route.queryParams.map((params) => { if (isNotEmpty(params.view) && hasValue(params.view)) { @@ -270,6 +301,10 @@ export class SearchService implements OnDestroy { }); } + /** + * Changes the current view mode in the current URL + * @param {ViewMode} viewMode Mode to switch to + */ setViewMode(viewMode: ViewMode) { const navigationExtras: NavigationExtras = { queryParams: { view: viewMode }, @@ -279,12 +314,18 @@ export class SearchService implements OnDestroy { this.router.navigate([this.getSearchLink()], navigationExtras); } + /** + * @returns {string} The base path to the search page + */ getSearchLink(): string { const urlTree = this.router.parseUrl(this.router.url); const g: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET]; return '/' + g.toString(); } + /** + * Unsubscribe from the subscription + */ ngOnDestroy(): void { if (this.sub !== undefined) { this.sub.unsubscribe(); diff --git a/src/app/+search-page/search-settings/search-settings.component.html b/src/app/+search-page/search-settings/search-settings.component.html index 0f10345edf..d693196dae 100644 --- a/src/app/+search-page/search-settings/search-settings.component.html +++ b/src/app/+search-page/search-settings/search-settings.component.html @@ -1,22 +1,24 @@ -

{{ 'search.sidebar.settings.title' | translate}}

-
+ +

{{ 'search.sidebar.settings.title' | translate}}

+
{{ 'search.sidebar.settings.sort-by' | translate}}
-
+
-
-
{{ 'search.sidebar.settings.rpp' | translate}}
- - -
+
+
{{ 'search.sidebar.settings.rpp' | translate}}
+ +
+ \ No newline at end of file diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts index ba511bf15c..c1778b37f4 100644 --- a/src/app/+search-page/search-settings/search-settings.component.ts +++ b/src/app/+search-page/search-settings/search-settings.component.ts @@ -1,34 +1,35 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { SearchService } from '../search-service/search.service'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; import { PaginatedSearchOptions } from '../paginated-search-options.model'; import { SearchFilterService } from '../search-filters/search-filter/search-filter.service'; +import { Observable } from 'rxjs/Observable'; @Component({ selector: 'ds-search-settings', styleUrls: ['./search-settings.component.scss'], templateUrl: './search-settings.component.html' }) + +/** + * This component represents the part of the search sidebar that contains the general search settings. + */ export class SearchSettingsComponent implements OnInit { - @Input() searchOptions: PaginatedSearchOptions; /** - * Declare SortDirection enumeration to use it in the template + * The configuration for the current paginated search results */ - public sortDirections = SortDirection; - /** - * Number of items per page. - */ - public pageSize; - @Input() public pageSizeOptions; + searchOptions$: Observable; - query: string; - page: number; - direction: SortDirection; - field: string; - currentParams = {}; + /** + * All sort options that are shown in the settings + */ searchOptionPossibilities = [new SortOptions('score', SortDirection.DESC), new SortOptions('dc.title', SortDirection.ASC), new SortOptions('dc.title', SortDirection.DESC)]; + + /** + * Default values for the Search Options + */ defaults = { pagination: { id: 'search-results-pagination', @@ -38,22 +39,24 @@ export class SearchSettingsComponent implements OnInit { query: '', scope: '' }; + constructor(private service: SearchService, private route: ActivatedRoute, private router: Router, private filterService: SearchFilterService) { } + /** + * Initialize paginated search options + */ ngOnInit(): void { - this.filterService.getPaginatedSearchOptions(this.defaults).subscribe((options) => { - this.direction = options.sort.direction; - this.field = options.sort.field; - this.searchOptions = options; - this.pageSize = options.pagination.pageSize; - this.pageSizeOptions = options.pagination.pageSizeOptions - }) + this.searchOptions$ = this.filterService.getPaginatedSearchOptions(this.defaults); } + /** + * Method to change the current page size (results per page) + * @param {Event} event Change event containing the new page size value + */ reloadRPP(event: Event) { const value = (event.target as HTMLInputElement).value; const navigationExtras: NavigationExtras = { @@ -65,6 +68,10 @@ export class SearchSettingsComponent implements OnInit { this.router.navigate([ '/search' ], navigationExtras); } + /** + * Method to change the current sort field and direction + * @param {Event} event Change event containing the sort direction and sort field + */ reloadOrder(event: Event) { const values = (event.target as HTMLInputElement).value.split(','); const navigationExtras: NavigationExtras = { diff --git a/src/app/+search-page/search-sidebar/search-sidebar.actions.ts b/src/app/+search-page/search-sidebar/search-sidebar.actions.ts index f393bc10b3..84a34b2790 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.actions.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.actions.ts @@ -17,14 +17,23 @@ export const SearchSidebarActionTypes = { }; /* tslint:disable:max-classes-per-file */ +/** + * Used to collapse the sidebar + */ export class SearchSidebarCollapseAction implements Action { type = SearchSidebarActionTypes.COLLAPSE; } +/** + * Used to expand the sidebar + */ export class SearchSidebarExpandAction implements Action { type = SearchSidebarActionTypes.EXPAND; } +/** + * Used to collapse the sidebar when it's expanded and expand it when it's collapsed + */ export class SearchSidebarToggleAction implements Action { type = SearchSidebarActionTypes.TOGGLE; } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.ts b/src/app/+search-page/search-sidebar/search-sidebar.component.ts index 946048462a..a160ff6cf6 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.ts @@ -12,7 +12,18 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; templateUrl: './search-sidebar.component.html', }) +/** + * Component representing the sidebar on the search page + */ export class SearchSidebarComponent { + + /** + * The total amount of result + */ @Input() resultCount; + + /** + * Emits event when the user clicks a button to open or close the sidebar + */ @Output() toggleSidebar = new EventEmitter(); } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.effects.ts b/src/app/+search-page/search-sidebar/search-sidebar.effects.ts index b65010b6e0..758ef2320b 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.effects.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.effects.ts @@ -5,6 +5,9 @@ import * as fromRouter from '@ngrx/router-store'; import { SearchSidebarCollapseAction } from './search-sidebar.actions'; import { URLBaser } from '../../core/url-baser/url-baser'; +/** + * Makes sure that if the user navigates to another route, the sidebar is collapsed + */ @Injectable() export class SearchSidebarEffects { private previousPath: string; diff --git a/src/app/+search-page/search-sidebar/search-sidebar.reducer.ts b/src/app/+search-page/search-sidebar/search-sidebar.reducer.ts index 81d9069238..a01f0ff6d6 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.reducer.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.reducer.ts @@ -1,5 +1,8 @@ import { SearchSidebarAction, SearchSidebarActionTypes } from './search-sidebar.actions'; +/** + * Interface that represents the state of the sidebar + */ export interface SearchSidebarState { sidebarCollapsed: boolean; } @@ -8,6 +11,12 @@ const initialState: SearchSidebarState = { sidebarCollapsed: true }; +/** + * Performs a search sidebar action on the current state + * @param {SearchSidebarState} state The state before the action is performed + * @param {SearchSidebarAction} action The action that should be performed + * @returns {SearchSidebarState} The state after the action is performed + */ export function sidebarReducer(state = initialState, action: SearchSidebarAction): SearchSidebarState { switch (action.type) { diff --git a/src/app/+search-page/search-sidebar/search-sidebar.service.ts b/src/app/+search-page/search-sidebar/search-sidebar.service.ts index 3a17dc87ab..47749d9e0c 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.service.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.service.ts @@ -9,27 +9,47 @@ import { HostWindowService } from '../../shared/host-window.service'; const sidebarStateSelector = (state: AppState) => state.searchSidebar; const sidebarCollapsedSelector = createSelector(sidebarStateSelector, (sidebar: SearchSidebarState) => sidebar.sidebarCollapsed); +/** + * Service that performs all actions that have to do with the search sidebar + */ @Injectable() export class SearchSidebarService { + /** + * Emits true is the current screen size is mobile + */ private isXsOrSm$: Observable; - private isCollapsdeInStored: Observable; + + /** + * Emits true is the sidebar's state in the store is currently collapsed + */ + private isCollapsedInStore: Observable; constructor(private store: Store, private windowService: HostWindowService) { this.isXsOrSm$ = this.windowService.isXsOrSm(); - this.isCollapsdeInStored = this.store.select(sidebarCollapsedSelector); + this.isCollapsedInStore = this.store.select(sidebarCollapsedSelector); } + /** + * Checks if the sidebar should currently be collapsed + * @returns {Observable} Emits true if our screen size is mobile or when the state in the store is currently collapsed + */ get isCollapsed(): Observable { return Observable.combineLatest( this.isXsOrSm$, - this.isCollapsdeInStored, + this.isCollapsedInStore, (mobile, store) => mobile ? store : true); } + /** + * Dispatches a collapse action to the store + */ public collapse(): void { this.store.dispatch(new SearchSidebarCollapseAction()); } + /** + * Dispatches a expand action to the store + */ public expand(): void { this.store.dispatch(new SearchSidebarExpandAction()); } diff --git a/src/app/core/cache/response-cache.models.ts b/src/app/core/cache/response-cache.models.ts index dbf8c1760d..13b1967911 100644 --- a/src/app/core/cache/response-cache.models.ts +++ b/src/app/core/cache/response-cache.models.ts @@ -1,8 +1,6 @@ import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model'; import { RequestError } from '../data/request.models'; -import { BrowseEntry } from '../shared/browse-entry.model'; import { PageInfo } from '../shared/page-info.model'; -import { BrowseDefinition } from '../shared/browse-definition.model'; import { ConfigObject } from '../shared/config/config.model'; import { FacetValue } from '../../+search-page/search-service/facet-value.model'; import { SearchFilterConfig } from '../../+search-page/search-service/search-filter-config.model'; @@ -10,8 +8,6 @@ import { RegistryMetadataschemasResponse } from '../registry/registry-metadatasc import { MetadataSchema } from '../metadata/metadataschema.model'; import { RegistryMetadatafieldsResponse } from '../registry/registry-metadatafields-response.model'; import { RegistryBitstreamformatsResponse } from '../registry/registry-bitstreamformats-response.model'; -import { AuthTokenInfo } from '../auth/models/auth-token-info.model'; -import { NormalizedAuthStatus } from '../auth/models/normalized-auth-status.model'; import { AuthStatus } from '../auth/models/auth-status.model'; /* tslint:disable:max-classes-per-file */