Intermediate commit

This commit is contained in:
Giuseppe Digilio
2019-03-11 20:07:11 +01:00
parent 11e77086ae
commit 013d464294
50 changed files with 181 additions and 167 deletions

View File

@@ -1,7 +1,6 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component'; import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
import { NormalizedCollection } from '../../core/cache/models/normalized-collection.model';
import { Collection } from '../../core/shared/collection.model'; import { Collection } from '../../core/shared/collection.model';
import { CollectionDataService } from '../../core/data/collection-data.service'; import { CollectionDataService } from '../../core/data/collection-data.service';

View File

@@ -1,6 +1,6 @@
import { fadeIn, fadeInOut } from '../../shared/animations/fade'; import { fadeIn, fadeInOut } from '../../shared/animations/fade';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { Item } from '../../core/shared/item.model'; import { Item } from '../../core/shared/item.model';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';

View File

@@ -97,4 +97,8 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService {
) )
} }
public getCurrentView(): Observable<string> {
return this.routeService.getQueryParameterValue('view');
}
} }

View File

@@ -5,7 +5,8 @@
<ds-search-sidebar *ngIf="!(isXsOrSm$ | async)" class="col-3 sidebar-md-sticky" <ds-search-sidebar *ngIf="!(isXsOrSm$ | async)" class="col-3 sidebar-md-sticky"
id="search-sidebar" id="search-sidebar"
[configurationList]="(configurationList$ | async)" [configurationList]="(configurationList$ | async)"
[resultCount]="(resultsRD$ | async)?.payload.totalElements"></ds-search-sidebar> [resultCount]="(resultsRD$ | async)?.payload.totalElements"
[viewModeList]="viewModeList"></ds-search-sidebar>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<ds-search-form id="mydspace-form" <ds-search-form id="mydspace-form"
[query]="(searchOptions$ | async)?.query" [query]="(searchOptions$ | async)?.query"
@@ -27,7 +28,7 @@
</ds-search-sidebar> </ds-search-sidebar>
<div id="mydspace-content" class="col-12"> <div id="mydspace-content" class="col-12">
<div class="d-block d-md-none search-controls clearfix"> <div class="d-block d-md-none search-controls clearfix">
<ds-view-mode-switch></ds-view-mode-switch> <ds-view-mode-switch [viewModeList]="viewModeList"></ds-view-mode-switch>
<button (click)="openSidebar()" aria-controls="#mydspace-body" <button (click)="openSidebar()" aria-controls="#mydspace-body"
class="btn btn-outline-primary float-right open-sidebar"><i class="btn btn-outline-primary float-right open-sidebar"><i
class="fas fa-sliders"></i> {{"search.sidebar.open" class="fas fa-sliders"></i> {{"search.sidebar.open"

View File

@@ -1,13 +1,14 @@
import { ChangeDetectionStrategy, Component, Inject, InjectionToken, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, InjectionToken, OnInit } from '@angular/core';
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { switchMap, } from 'rxjs/operators'; import { switchMap, tap, } from 'rxjs/operators';
import { PaginatedList } from '../core/data/paginated-list'; import { PaginatedList } from '../core/data/paginated-list';
import { RemoteData } from '../core/data/remote-data'; import { RemoteData } from '../core/data/remote-data';
import { DSpaceObject } from '../core/shared/dspace-object.model'; import { DSpaceObject } from '../core/shared/dspace-object.model';
import { pushInOut } from '../shared/animations/push'; import { pushInOut } from '../shared/animations/push';
import { HostWindowService } from '../shared/host-window.service'; import { HostWindowService } from '../shared/host-window.service';
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model'; import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
import { SearchFilterService } from '../+search-page/search-filters/search-filter/search-filter.service';
import { SearchService } from '../+search-page/search-service/search.service'; import { SearchService } from '../+search-page/search-service/search.service';
import { SearchSidebarService } from '../+search-page/search-sidebar/search-sidebar.service'; import { SearchSidebarService } from '../+search-page/search-sidebar/search-sidebar.service';
import { hasValue } from '../shared/empty.util'; import { hasValue } from '../shared/empty.util';
@@ -18,6 +19,7 @@ import { SearchConfigurationOption } from '../+search-page/search-switch-configu
import { RoleType } from '../core/roles/role-types'; import { RoleType } from '../core/roles/role-types';
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service'; import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'; import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
import { ViewMode } from '../core/shared/view-mode.model';
export const MYDSPACE_ROUTE = '/mydspace'; export const MYDSPACE_ROUTE = '/mydspace';
export const SEARCH_CONFIG_SERVICE: InjectionToken<SearchConfigurationService> = new InjectionToken<SearchConfigurationService>('searchConfigurationService'); export const SEARCH_CONFIG_SERVICE: InjectionToken<SearchConfigurationService> = new InjectionToken<SearchConfigurationService>('searchConfigurationService');
@@ -79,10 +81,11 @@ export class MyDSpacePageComponent implements OnInit {
roleTypeEnum = RoleType; roleTypeEnum = RoleType;
viewModeList = [ViewMode.List, ViewMode.Detail];
constructor(private service: SearchService, constructor(private service: SearchService,
private sidebarService: SearchSidebarService, private sidebarService: SearchSidebarService,
private windowService: HostWindowService, private windowService: HostWindowService,
private filterService: SearchFilterService,
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) { @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) {
this.isXsOrSm$ = this.windowService.isXsOrSm(); this.isXsOrSm$ = this.windowService.isXsOrSm();
this.service.setServiceOptions(MyDSpaceResponseParsingService, true); this.service.setServiceOptions(MyDSpaceResponseParsingService, true);
@@ -97,10 +100,11 @@ export class MyDSpacePageComponent implements OnInit {
*/ */
ngOnInit(): void { ngOnInit(): void {
this.configurationList$ = this.searchConfigService.getAvailableConfigurationOptions(); this.configurationList$ = this.searchConfigService.getAvailableConfigurationOptions();
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions; this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
this.sub = this.searchOptions$.pipe( this.sub = this.searchOptions$.pipe(
switchMap((options) => this.service.search(options).pipe(getSucceededRemoteData()))) tap(() => this.resultsRD$.next(null)),
switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getSucceededRemoteData())))
.subscribe((results) => { .subscribe((results) => {
this.resultsRD$.next(results); this.resultsRD$.next(results);
}); });

View File

@@ -1,5 +1,5 @@
import { DSpaceObject } from '../core/shared/dspace-object.model'; import { DSpaceObject } from '../core/shared/dspace-object.model';
import { MetadataMap } from '../core/shared/metadata.interfaces'; import { MetadataMap } from '../core/shared/metadata.models';
import { ListableObject } from '../shared/object-collection/shared/listable-object.model'; import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
/** /**

View File

@@ -7,6 +7,6 @@
[hideGear]="true"> [hideGear]="true">
</ds-viewable-collection> </ds-viewable-collection>
</div> </div>
<ds-loading *ngIf="!searchResults || searchResults?.isLoading" message="{{'loading.mydspace-results' | translate}}"></ds-loading> <ds-loading *ngIf="isLoading()" message="{{'loading.mydspace-results' | translate}}"></ds-loading>
<ds-error *ngIf="searchResults?.hasFailed && (!searchResults?.error || searchResults?.error?.statusCode != 400)" message="{{'error.search-results' | translate}}"></ds-error> <ds-error *ngIf="searchResults?.hasFailed && (!searchResults?.error || searchResults?.error?.statusCode != 400)" message="{{'error.search-results' | translate}}"></ds-error>
<h3 *ngIf="searchResults?.payload?.page.length == 0" class="text-center text-muted" ><span>{{'mydspace.results.no-results' | translate}}</span></h3> <h3 *ngIf="searchResults?.payload?.page.length == 0" class="text-center text-muted" ><span>{{'mydspace.results.no-results' | translate}}</span></h3>

View File

@@ -8,6 +8,7 @@ import { MyDSpaceResult } from '../my-dspace-result.model';
import { SearchOptions } from '../../+search-page/search-options.model'; import { SearchOptions } from '../../+search-page/search-options.model';
import { PaginatedList } from '../../core/data/paginated-list'; import { PaginatedList } from '../../core/data/paginated-list';
import { ViewMode } from '../../core/shared/view-mode.model'; import { ViewMode } from '../../core/shared/view-mode.model';
import { isEmpty } from '../../shared/empty.util';
/** /**
* This component renders a simple item page. * This component renders a simple item page.
@@ -28,6 +29,9 @@ export class MyDSpaceResultsComponent {
@Input() sortConfig: SortOptions; @Input() sortConfig: SortOptions;
@Input() viewMode: ViewMode; @Input() viewMode: ViewMode;
public hasBorder = true; hasBorder = true;
isLoading() {
return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading;
}
} }

View File

@@ -1,13 +1,7 @@
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { import { ActivatedRoute, NavigationExtras, PRIMARY_OUTLET, Router, UrlSegmentGroup } from '@angular/router';
ActivatedRoute, import { first, map, switchMap } from 'rxjs/operators';
NavigationExtras,
PRIMARY_OUTLET,
Router,
UrlSegmentGroup
} from '@angular/router';
import { distinctUntilChanged, filter, first, map, switchMap, take, tap } from 'rxjs/operators';
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
import { import {
FacetConfigSuccessResponse, FacetConfigSuccessResponse,
@@ -22,11 +16,7 @@ import { RequestService } from '../../core/data/request.service';
import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { GenericConstructor } from '../../core/shared/generic-constructor'; import { GenericConstructor } from '../../core/shared/generic-constructor';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { import { configureRequest, getResponseFromEntry, getSucceededRemoteData } from '../../core/shared/operators';
configureRequest, filterSuccessfulResponses,
getResponseFromEntry,
getSucceededRemoteData
} from '../../core/shared/operators';
import { URLCombiner } from '../../core/url-combiner/url-combiner'; import { URLCombiner } from '../../core/url-combiner/url-combiner';
import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util'; import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util';
import { NormalizedSearchResult } from '../normalized-search-result.model'; import { NormalizedSearchResult } from '../normalized-search-result.model';
@@ -47,6 +37,7 @@ import { CommunityDataService } from '../../core/data/community-data.service';
import { ViewMode } from '../../core/shared/view-mode.model'; import { ViewMode } from '../../core/shared/view-mode.model';
import { ResourceType } from '../../core/shared/resource-type'; import { ResourceType } from '../../core/shared/resource-type';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { RouteService } from '../../shared/services/route.service';
/** /**
* Service that performs all general actions that have to do with the search page * Service that performs all general actions that have to do with the search page
@@ -80,6 +71,7 @@ export class SearchService implements OnDestroy {
constructor(private router: Router, constructor(private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private routeService: RouteService,
protected requestService: RequestService, protected requestService: RequestService,
private rdb: RemoteDataBuildService, private rdb: RemoteDataBuildService,
private halService: HALEndpointService, private halService: HALEndpointService,
@@ -235,7 +227,6 @@ export class SearchService implements OnDestroy {
* @returns {Observable<RemoteData<PaginatedList<FacetValue>>>} Emits the given page of facet values * @returns {Observable<RemoteData<PaginatedList<FacetValue>>>} Emits the given page of facet values
*/ */
getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string): Observable<RemoteData<PaginatedList<FacetValue>>> { getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string): Observable<RemoteData<PaginatedList<FacetValue>>> {
console.log('getFacetValuesFor');
const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix + filterConfig.name).pipe( const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix + filterConfig.name).pipe(
map((url: string) => { map((url: string) => {
const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`]; const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`];
@@ -323,9 +314,9 @@ export class SearchService implements OnDestroy {
* @returns {Observable<ViewMode>} The current view mode * @returns {Observable<ViewMode>} The current view mode
*/ */
getViewMode(): Observable<ViewMode> { getViewMode(): Observable<ViewMode> {
return this.route.queryParams.pipe(map((params) => { return this.routeService.getQueryParamMap().pipe(map((params) => {
if (isNotEmpty(params.view) && hasValue(params.view)) { if (isNotEmpty(params.get('view')) && hasValue(params.get('view'))) {
return params.view; return params.get('view');
} else { } else {
return ViewMode.List; return ViewMode.List;
} }
@@ -337,12 +328,21 @@ export class SearchService implements OnDestroy {
* @param {ViewMode} viewMode Mode to switch to * @param {ViewMode} viewMode Mode to switch to
*/ */
setViewMode(viewMode: ViewMode) { setViewMode(viewMode: ViewMode) {
const navigationExtras: NavigationExtras = { this.routeService.getQueryParameterValue('pageSize').pipe(first())
queryParams: { view: viewMode }, .subscribe((pageSize) => {
queryParamsHandling: 'merge' let queryParams = { view: viewMode };
}; if (viewMode === ViewMode.Detail) {
queryParams = Object.assign(queryParams, {pageSize: '1'});
} else if (pageSize === '1') {
queryParams = Object.assign(queryParams, {pageSize: '10'});
}
const navigationExtras: NavigationExtras = {
queryParams: queryParams,
queryParamsHandling: 'merge'
};
this.router.navigate([this.getSearchLink()], navigationExtras); this.router.navigate([this.getSearchLink()], navigationExtras);
})
} }
/** /**

View File

@@ -54,7 +54,7 @@ export class SearchSettingsComponent implements OnInit {
}, },
queryParamsHandling: 'merge' queryParamsHandling: 'merge'
}; };
this.router.navigate([ '/search' ], navigationExtras); this.router.navigate([ this.service.getSearchLink() ], navigationExtras);
} }
/** /**
@@ -71,6 +71,6 @@ export class SearchSettingsComponent implements OnInit {
}, },
queryParamsHandling: 'merge' queryParamsHandling: 'merge'
}; };
this.router.navigate([ '/search' ], navigationExtras); this.router.navigate([ this.service.getSearchLink() ], navigationExtras);
} }
} }

View File

@@ -8,7 +8,7 @@
</button> </button>
</div> </div>
<div id="search-sidebar-content"> <div id="search-sidebar-content">
<ds-view-mode-switch class="d-none d-md-block"></ds-view-mode-switch> <ds-view-mode-switch [viewModeList]="viewModeList" class="d-none d-md-block"></ds-view-mode-switch>
<div class="sidebar-content"> <div class="sidebar-content">
<ds-search-switch-configuration *ngIf="configurationList" [configurationList]="configurationList"></ds-search-switch-configuration> <ds-search-switch-configuration *ngIf="configurationList" [configurationList]="configurationList"></ds-search-switch-configuration>
<ds-search-filters></ds-search-filters> <ds-search-filters></ds-search-filters>

View File

@@ -29,6 +29,11 @@ export class SearchSidebarComponent {
*/ */
@Input() resultCount; @Input() resultCount;
/**
* The list of available view mode options
*/
@Input() viewModeList;
/** /**
* Emits event when the user clicks a button to open or close the sidebar * Emits event when the user clicks a button to open or close the sidebar
*/ */

View File

@@ -1,43 +1,27 @@
import {Observable, of, of as observableOf} from 'rxjs';
import {
distinctUntilChanged,
filter,
first,
map,
startWith,
switchMap,
take,
withLatestFrom
} from 'rxjs/operators';
import { Inject, Injectable, Optional } from '@angular/core'; import { Inject, Injectable, Optional } from '@angular/core';
import { PRIMARY_OUTLET, Router, UrlSegmentGroup, UrlTree } from '@angular/router'; import { PRIMARY_OUTLET, Router, UrlSegmentGroup, UrlTree } from '@angular/router';
import { HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens'; import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
import { Observable, of as observableOf } from 'rxjs';
import { distinctUntilChanged, filter, map, startWith, switchMap, take, withLatestFrom } from 'rxjs/operators';
import { RouterReducerState } from '@ngrx/router-store'; import { RouterReducerState } from '@ngrx/router-store';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { CookieAttributes } from 'js-cookie'; import { CookieAttributes } from 'js-cookie';
import { EPerson } from '../eperson/models/eperson.model'; import { EPerson } from '../eperson/models/eperson.model';
import { AuthRequestService } from './auth-request.service'; import { AuthRequestService } from './auth-request.service';
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
import { AuthStatus } from './models/auth-status.model'; import { AuthStatus } from './models/auth-status.model';
import { AuthTokenInfo, TOKENITEM } from './models/auth-token-info.model'; import { AuthTokenInfo, TOKENITEM } from './models/auth-token-info.model';
import { isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../shared/empty.util'; import { isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../shared/empty.util';
import { CookieService } from '../../shared/services/cookie.service'; import { CookieService } from '../../shared/services/cookie.service';
import { import { getAuthenticationToken, getRedirectUrl, isAuthenticated, isTokenRefreshing } from './selectors';
getAuthenticationToken,
getRedirectUrl,
isAuthenticated,
isTokenRefreshing
} from './selectors';
import { AppState, routerStateSelector } from '../../app.reducer'; import { AppState, routerStateSelector } from '../../app.reducer';
import { ResetAuthenticationMessagesAction, SetRedirectUrlAction } from './auth.actions'; import { ResetAuthenticationMessagesAction, SetRedirectUrlAction } from './auth.actions';
import { NativeWindowRef, NativeWindowService } from '../../shared/services/window.service'; import { NativeWindowRef, NativeWindowService } from '../../shared/services/window.service';
import { Base64EncodeUrl } from '../../shared/utils/encode-decode.util'; import { Base64EncodeUrl } from '../../shared/utils/encode-decode.util';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { NormalizedEPerson } from '../eperson/models/normalized-eperson.model';
export const LOGIN_ROUTE = '/login'; export const LOGIN_ROUTE = '/login';
export const LOGOUT_ROUTE = '/logout'; export const LOGOUT_ROUTE = '/logout';

View File

@@ -4,7 +4,6 @@ import { filter, map, take } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { NormalizedCollection } from '../cache/models/normalized-collection.model';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
import { Collection } from '../shared/collection.model'; import { Collection } from '../shared/collection.model';

View File

@@ -1,9 +1,8 @@
import { filter, mergeMap, take } from 'rxjs/operators'; import { filter, take } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
import { Community } from '../shared/community.model'; import { Community } from '../shared/community.model';
@@ -12,7 +11,7 @@ import { RequestService } from './request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FindAllOptions, FindAllRequest } from './request.models'; import { FindAllOptions, FindAllRequest } from './request.models';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { PaginatedList } from './paginated-list'; import { PaginatedList } from './paginated-list';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';

View File

@@ -1,9 +1,7 @@
import { Operation } from 'fast-json-patch/lib/core'; import { Operation } from 'fast-json-patch/lib/core';
import { compare } from 'fast-json-patch'; import { compare } from 'fast-json-patch';
import { ChangeAnalyzer } from './change-analyzer'; import { ChangeAnalyzer } from './change-analyzer';
import { NormalizedDSpaceObject } from '../cache/models/normalized-dspace-object.model';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { DSpaceObject } from '../shared/dspace-object.model';
import { CacheableObject } from '../cache/object-cache.reducer'; import { CacheableObject } from '../cache/object-cache.reducer';
import { NormalizedObject } from '../cache/models/normalized-object.model'; import { NormalizedObject } from '../cache/models/normalized-object.model';

View File

@@ -7,7 +7,7 @@ import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model'; import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model';
import { MetadataMap, MetadataValue } from '../shared/metadata.interfaces'; import { MetadataMap, MetadataValue } from '../shared/metadata.models';
@Injectable() @Injectable()
export class MyDSpaceResponseParsingService implements ResponseParsingService { export class MyDSpaceResponseParsingService implements ResponseParsingService {
@@ -17,7 +17,7 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService {
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
// fallback for unexpected empty response // fallback for unexpected empty response
const emptyPayload = { const emptyPayload = {
_embedded : { _embedded: {
objects: [] objects: []
} }
}; };
@@ -28,8 +28,11 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService {
const mdMap: MetadataMap = {}; const mdMap: MetadataMap = {};
if (hhObject) { if (hhObject) {
for (const key of Object.keys(hhObject)) { for (const key of Object.keys(hhObject)) {
const value: MetadataValue = { value: hhObject[key].join('...'), language: null }; const value: MetadataValue = Object.assign(new MetadataValue(), {
mdMap[key] = [ value ]; value: hhObject[key].join('...'),
language: null
});
mdMap[key] = [value];
} }
} }
return mdMap; return mdMap;
@@ -50,10 +53,30 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService {
.filter((object) => hasValue(object._embedded)) .filter((object) => hasValue(object._embedded))
.map((object, index) => Object.assign({}, object, { .map((object, index) => Object.assign({}, object, {
rObject: dsoSelfLinks[index], rObject: dsoSelfLinks[index],
hitHighlights: hitHighlights[index] hitHighlights: hitHighlights[index],
_embedded: this.filterEmbeddedObjects(object)
})); }));
payload.objects = objects; payload.objects = objects;
const deserialized = new DSpaceRESTv2Serializer(SearchQueryResponse).deserialize(payload); const deserialized = new DSpaceRESTv2Serializer(SearchQueryResponse).deserialize(payload);
return new SearchSuccessResponse(deserialized, data.statusCode, data.statusText, this.dsoParser.processPageInfo(payload)); return new SearchSuccessResponse(deserialized, data.statusCode, data.statusText, this.dsoParser.processPageInfo(payload));
} }
protected filterEmbeddedObjects(object) {
const allowedEmbeddedKeys = ['submitter', 'item', 'workspaceitem', 'workflowitem'];
if (object._embedded.rObject && object._embedded.rObject._embedded) {
return Object.assign({}, object._embedded, {
rObject: Object.assign({}, object._embedded.rObject, {
_embedded: Object.keys(object._embedded.rObject._embedded)
.filter((key) => allowedEmbeddedKeys.includes(key))
.reduce((obj, key) => {
obj[key] = object._embedded.rObject._embedded[key];
return obj;
}, {})
})
});
} else {
return object;
}
}
} }

View File

@@ -253,11 +253,10 @@ export class RequestService {
} }
/** /**
* This method will store the href of every GET request that gets configured in a local variable, and * This method remove requests that are on their way to the store.
* remove it as soon as it can be found in the store.
*/ */
private clearRequestsOnTheirWayToTheStore(request: GetRequest) { private clearRequestsOnTheirWayToTheStore(request: GetRequest) {
this.store.pipe(select(this.entryFromUUIDSelector(request.uuid)), this.getByHref(request.href).pipe(
find((re: RequestEntry) => hasValue(re))) find((re: RequestEntry) => hasValue(re)))
.subscribe((re: RequestEntry) => { .subscribe((re: RequestEntry) => {
if (!re.responsePending) { if (!re.responsePending) {
@@ -265,6 +264,7 @@ export class RequestService {
} }
}); });
} }
/** /**
* Dispatch commit action to send all changes (for a certain method) to the server (buffer) * Dispatch commit action to send all changes (for a certain method) to the server (buffer)
* @param {RestRequestMethod} method RestRequestMethod for which the changes should be committed * @param {RestRequestMethod} method RestRequestMethod for which the changes should be committed

View File

@@ -14,9 +14,6 @@ export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implement
@autoserialize @autoserialize
public handle: string; public handle: string;
@autoserialize
public name: string;
@autoserializeAs(NormalizedGroup) @autoserializeAs(NormalizedGroup)
groups: Group[]; groups: Group[];

View File

@@ -7,13 +7,13 @@ import { BaseResponseParsingService } from '../data/base-response-parsing.servic
import { GLOBAL_CONFIG } from '../../../config'; import { GLOBAL_CONFIG } from '../../../config';
import { GlobalConfig } from '../../../config/global-config.interface'; import { GlobalConfig } from '../../../config/global-config.interface';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { NormalizedSubmissionObjectFactory } from '../submission/normalized-submission-object-factory';
import { ErrorResponse, MessageResponse, RestResponse } from '../cache/response.models'; import { ErrorResponse, MessageResponse, RestResponse } from '../cache/response.models';
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
@Injectable() @Injectable()
export class MessageResponseParsingService extends BaseResponseParsingService implements ResponseParsingService { export class MessageResponseParsingService extends BaseResponseParsingService implements ResponseParsingService {
protected objectFactory = NormalizedSubmissionObjectFactory; protected objectFactory = NormalizedObjectFactory;
protected toCache = false; protected toCache = false;
constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig, constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,

View File

@@ -7,7 +7,6 @@ import { Observable } from 'rxjs';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { NormalizedClaimedTask } from './models/normalized-claimed-task-object.model';
import { ClaimedTask } from './models/claimed-task-object.model'; import { ClaimedTask } from './models/claimed-task-object.model';
import { TasksService } from './tasks.service'; import { TasksService } from './tasks.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
@@ -17,7 +16,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
@Injectable() @Injectable()
export class ClaimedTaskDataService extends TasksService<NormalizedClaimedTask, ClaimedTask> { export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
protected linkPath = 'claimedtasks'; protected linkPath = 'claimedtasks';
protected forceBypassCache = true; protected forceBypassCache = true;
@@ -30,7 +29,7 @@ export class ClaimedTaskDataService extends TasksService<NormalizedClaimedTask,
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient, protected http: HttpClient,
protected comparator: DSOChangeAnalyzer) { protected comparator: DSOChangeAnalyzer<ClaimedTask>) {
super(); super();
} }

View File

@@ -9,7 +9,7 @@ import { ResourceType } from '../../shared/resource-type';
*/ */
@mapsTo(ClaimedTask) @mapsTo(ClaimedTask)
@inheritSerialization(NormalizedTaskObject) @inheritSerialization(NormalizedTaskObject)
export class NormalizedClaimedTask extends NormalizedTaskObject { export class NormalizedClaimedTask extends NormalizedTaskObject<ClaimedTask> {
/** /**
* The task identifier * The task identifier

View File

@@ -9,7 +9,7 @@ import { ResourceType } from '../../shared/resource-type';
*/ */
@mapsTo(PoolTask) @mapsTo(PoolTask)
@inheritSerialization(NormalizedTaskObject) @inheritSerialization(NormalizedTaskObject)
export class NormalizedPoolTask extends NormalizedTaskObject { export class NormalizedPoolTask extends NormalizedTaskObject<PoolTask> {
/** /**
* The task identifier * The task identifier

View File

@@ -3,13 +3,14 @@ import { mapsTo, relationship } from '../../cache/builders/build-decorators';
import { ResourceType } from '../../shared/resource-type'; import { ResourceType } from '../../shared/resource-type';
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model'; import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
import { TaskObject } from './task-object.model'; import { TaskObject } from './task-object.model';
import { DSpaceObject } from '../../shared/dspace-object.model';
/** /**
* An abstract model class for a DSpaceObject. * An abstract model class for a DSpaceObject.
*/ */
@mapsTo(TaskObject) @mapsTo(TaskObject)
@inheritSerialization(NormalizedDSpaceObject) @inheritSerialization(NormalizedDSpaceObject)
export abstract class NormalizedTaskObject extends NormalizedDSpaceObject { export abstract class NormalizedTaskObject<T extends DSpaceObject> extends NormalizedDSpaceObject<T> {
/** /**
* The task identifier * The task identifier

View File

@@ -7,7 +7,6 @@ import { Store } from '@ngrx/store';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { NormalizedPoolTask } from './models/normalized-pool-task-object.model';
import { PoolTask } from './models/pool-task-object.model'; import { PoolTask } from './models/pool-task-object.model';
import { TasksService } from './tasks.service'; import { TasksService } from './tasks.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
@@ -17,7 +16,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
@Injectable() @Injectable()
export class PoolTaskDataService extends TasksService<NormalizedPoolTask, PoolTask> { export class PoolTaskDataService extends TasksService<PoolTask> {
protected linkPath = 'pooltasks'; protected linkPath = 'pooltasks';
protected forceBypassCache = true; protected forceBypassCache = true;
@@ -30,7 +29,7 @@ export class PoolTaskDataService extends TasksService<NormalizedPoolTask, PoolTa
protected halService: HALEndpointService, protected halService: HALEndpointService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected http: HttpClient, protected http: HttpClient,
protected comparator: DSOChangeAnalyzer) { protected comparator: DSOChangeAnalyzer<PoolTask>) {
super(); super();
} }

View File

@@ -9,12 +9,11 @@ import { isNotEmpty } from '../../shared/empty.util';
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
import { ProcessTaskResponse } from './models/process-task-response'; import { ProcessTaskResponse } from './models/process-task-response';
import { RemoteDataError } from '../data/remote-data-error'; import { RemoteDataError } from '../data/remote-data-error';
import { NormalizedObject } from '../cache/models/normalized-object.model';
import { getResponseFromEntry } from '../shared/operators'; import { getResponseFromEntry } from '../shared/operators';
import { ErrorResponse, MessageResponse, RestResponse } from '../cache/response.models'; import { ErrorResponse, MessageResponse, RestResponse } from '../cache/response.models';
import { CacheableObject } from '../cache/object-cache.reducer'; import { CacheableObject } from '../cache/object-cache.reducer';
export abstract class TasksService<TNormalized extends NormalizedObject, TDomain extends CacheableObject> extends DataService<TNormalized, TDomain> { export abstract class TasksService<T extends CacheableObject> extends DataService<T> {
public getBrowseEndpoint(options: FindAllOptions): Observable<string> { public getBrowseEndpoint(options: FindAllOptions): Observable<string> {
return this.halService.getEndpoint(this.linkPath); return this.halService.getEndpoint(this.linkPath);
@@ -34,7 +33,7 @@ export abstract class TasksService<TNormalized extends NormalizedObject, TDomain
)); ));
const successResponses = responses.pipe( const successResponses = responses.pipe(
filter((response: RestResponse) => response.isSuccessful), filter((response: RestResponse) => response.isSuccessful),
map((response: MessageResponse) => new ProcessTaskResponse(response.isSuccessful)), map((response: MessageResponse) => new ProcessTaskResponse(response.isSuccessful)),
distinctUntilChanged() distinctUntilChanged()
); );
return observableMerge(errorResponses, successResponses); return observableMerge(errorResponses, successResponses);

View File

@@ -1,7 +1,7 @@
<ds-loading *ngIf="(loading$ | async)"></ds-loading> <ds-loading *ngIf="(loading$ | async)"></ds-loading>
<div *ngIf="!(loading$ | async)"> <div *ngIf="!(loading$ | async)">
<span class="dropdown-item-text">{{(user$ | async).name}}</span> <span class="dropdown-item-text">{{(user$ | async)?.name}} ({{(user$ | async)?.email}})</span>
<a class="dropdown-item" [routerLink]="['/mydspace']" routerLinkActive="active">{{'nav.mydspace' | translate}}</a> <a class="dropdown-item" [routerLink]="[mydspaceRoute]" routerLinkActive="active">{{'nav.mydspace' | translate}}</a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<ds-log-out></ds-log-out> <ds-log-out></ds-log-out>
</div> </div>

View File

@@ -6,6 +6,7 @@ import { select, Store } from '@ngrx/store';
import { EPerson } from '../../../core/eperson/models/eperson.model'; import { EPerson } from '../../../core/eperson/models/eperson.model';
import { AppState } from '../../../app.reducer'; import { AppState } from '../../../app.reducer';
import { getAuthenticatedUser, isAuthenticationLoading } from '../../../core/auth/selectors'; import { getAuthenticatedUser, isAuthenticationLoading } from '../../../core/auth/selectors';
import { MYDSPACE_ROUTE } from '../../../+my-dspace-page/my-dspace-page.component';
@Component({ @Component({
selector: 'ds-user-menu', selector: 'ds-user-menu',
@@ -26,6 +27,12 @@ export class UserMenuComponent implements OnInit {
*/ */
public user$: Observable<EPerson>; public user$: Observable<EPerson>;
/**
* The mydspace page route.
* @type {string}
*/
public mydspaceRoute = MYDSPACE_ROUTE;
constructor(private store: Store<AppState>) { constructor(private store: Store<AppState>) {
} }

View File

@@ -12,7 +12,7 @@ import {
ViewChildren ViewChildren
} from '@angular/core'; } from '@angular/core';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { hasValue, isNotEmpty, isNotUndefined } from '../empty.util'; import { hasValue, isNotEmpty } from '../empty.util';
import { InputSuggestion } from './input-suggestions.model'; import { InputSuggestion } from './input-suggestions.model';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

View File

@@ -12,7 +12,6 @@ import {
mergeMap, mergeMap,
reduce, reduce,
startWith, startWith,
tap,
withLatestFrom withLatestFrom
} from 'rxjs/operators'; } from 'rxjs/operators';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
@@ -77,31 +76,26 @@ export class MessageBoardComponent implements OnDestroy {
this.user$ = this.store.pipe( this.user$ = this.store.pipe(
select(getAuthenticatedUser), select(getAuthenticatedUser),
find((user: EPerson) => isNotEmpty(user)), find((user: EPerson) => isNotEmpty(user)),
map((user: EPerson) => user), map((user: EPerson) => user));
tap((u) => console.log(u)));
this.item$ = this.dso.item.pipe( this.item$ = this.dso.item.pipe(
find((rd: RemoteData<Item>) => (rd.hasSucceeded && isNotEmpty(rd.payload))), find((rd: RemoteData<Item>) => (rd.hasSucceeded && isNotEmpty(rd.payload))),
map((rd: RemoteData<Item>) => rd.payload), map((rd: RemoteData<Item>) => rd.payload));
tap((u) => console.log(u)));
this.submitter$ = (this.dso.submitter as Observable<RemoteData<EPerson[]>>).pipe( this.submitter$ = (this.dso.submitter as Observable<RemoteData<EPerson[]>>).pipe(
find((rd: RemoteData<EPerson>) => rd.hasSucceeded && isNotEmpty(rd.payload)), find((rd: RemoteData<EPerson>) => rd.hasSucceeded && isNotEmpty(rd.payload)),
map((rd: RemoteData<EPerson>) => rd.payload), map((rd: RemoteData<EPerson>) => rd.payload));
tap((u) => console.log(u)));
this.isSubmitter$ = combineLatest(this.user$, this.submitter$).pipe( this.isSubmitter$ = combineLatest(this.user$, this.submitter$).pipe(
filter(([user, submitter]) => isNotEmpty(user) && isNotEmpty(submitter)), filter(([user, submitter]) => isNotEmpty(user) && isNotEmpty(submitter)),
map(([user, submitter]) => user.uuid === submitter.uuid), map(([user, submitter]) => user.uuid === submitter.uuid));
tap((u) => console.log(u)));
this.messages$ = this.item$.pipe( this.messages$ = this.item$.pipe(
find((item: Item) => isNotEmpty(item)), find((item: Item) => isNotEmpty(item)),
flatMap((item: Item) => item.getBitstreamsByBundleName('MESSAGE')), flatMap((item: Item) => item.getBitstreamsByBundleName('MESSAGE')),
filter((bitStreams: Bitstream[]) => isNotEmpty(bitStreams)), filter((bitStreams: Bitstream[]) => isNotEmpty(bitStreams)),
startWith([]), startWith([]),
distinctUntilChanged(), distinctUntilChanged());
tap((u) => console.log(u)));
this.unreadMessages$ = this.messages$.pipe( this.unreadMessages$ = this.messages$.pipe(
filter((messages: Bitstream[]) => isNotEmpty(messages)), filter((messages: Bitstream[]) => isNotEmpty(messages)),
@@ -118,8 +112,7 @@ export class MessageBoardComponent implements OnDestroy {
this.itemUUID$ = this.item$.pipe( this.itemUUID$ = this.item$.pipe(
find((item: Item) => isNotEmpty(item)), find((item: Item) => isNotEmpty(item)),
map((item: Item) => item.uuid), map((item: Item) => item.uuid));
tap((u) => console.log(u)));
} }

View File

@@ -22,7 +22,7 @@
ngbTooltip="{{'submission.workflow.tasks.claimed.return_help' | translate}}" ngbTooltip="{{'submission.workflow.tasks.claimed.return_help' | translate}}"
[disabled]="(processingReturnToPool$ | async)" [disabled]="(processingReturnToPool$ | async)"
(click)="returnToPool()"> (click)="returnToPool()">
<span *ngIf="(processingReturnToPool$ | async)"><i class='fa fa-circle-o-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span> <span *ngIf="(processingReturnToPool$ | async)"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
<span *ngIf="!(processingReturnToPool$ | async)"><i class="fa fa-undo"></i> {{'submission.workflow.tasks.claimed.return' | translate}}</span> <span *ngIf="!(processingReturnToPool$ | async)"><i class="fa fa-undo"></i> {{'submission.workflow.tasks.claimed.return' | translate}}</span>
</button> </button>

View File

@@ -13,7 +13,6 @@ import { NotificationOptions } from '../../notifications/models/notification-opt
import { isNotUndefined } from '../../empty.util'; import { isNotUndefined } from '../../empty.util';
import { Workflowitem } from '../../../core/submission/models/workflowitem.model'; import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { NormalizedClaimedTask } from '../../../core/tasks/models/normalized-claimed-task-object.model';
import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { MyDSpaceActionsComponent } from '../mydspace-actions';
import { ResourceType } from '../../../core/shared/resource-type'; import { ResourceType } from '../../../core/shared/resource-type';
@@ -23,7 +22,7 @@ import { ResourceType } from '../../../core/shared/resource-type';
templateUrl: './claimed-task-actions.component.html', templateUrl: './claimed-task-actions.component.html',
}) })
export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<ClaimedTask, NormalizedClaimedTask, ClaimedTaskDataService> implements OnInit { export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<ClaimedTask, ClaimedTaskDataService> implements OnInit {
@Input() object: ClaimedTask; @Input() object: ClaimedTask;
public workflowitem$: Observable<Workflowitem>; public workflowitem$: Observable<Workflowitem>;

View File

@@ -1,11 +1,5 @@
<a class="btn btn-light mt-1 mb-3" <button class="btn btn-primary mt-1 mb-3"
role="button" ngbTooltip="{{'submission.workflow.generic.view-help' | translate}}"
ngbTooltip="{{'submission.workflow.generic.view-help' | translate}}" [routerLink]="['/items/' + object.id]">
[href]="itemUrl">
<i class="fa fa-info-circle"></i> {{"submission.workflow.generic.view" | translate}} <i class="fa fa-info-circle"></i> {{"submission.workflow.generic.view" | translate}}
</a> </button>
<!--<button class="btn btn-primary mt-1 mb-3"-->
<!--ngbTooltip="{{'submission.workflow.generic.view-help' | translate}}"-->
<!--[routerLink]="['/items/' + object.id]">-->
<!--<i class="fa fa-info-circle"></i> {{"submission.workflow.generic.view" | translate}}-->
<!--</button>-->

View File

@@ -1,14 +1,10 @@
import { Component, Injector, Input, OnInit } from '@angular/core'; import { Component, Injector, Input } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { MyDSpaceActionsComponent } from '../mydspace-actions';
import { ResourceType } from '../../../core/shared/resource-type'; import { ResourceType } from '../../../core/shared/resource-type';
import { ItemDataService } from '../../../core/data/item-data.service'; import { ItemDataService } from '../../../core/data/item-data.service';
import { NormalizedItem } from '../../../core/cache/models/normalized-item.model';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { RoleService } from '../../../core/roles/role.service';
@Component({ @Component({
selector: 'ds-item-actions', selector: 'ds-item-actions',
@@ -16,23 +12,14 @@ import { RoleService } from '../../../core/roles/role.service';
templateUrl: './item-actions.component.html', templateUrl: './item-actions.component.html',
}) })
export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, NormalizedItem, ItemDataService> implements OnInit { export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDataService> {
@Input() object: Item; @Input() object: Item;
public isAdmin: Observable<boolean>;
public itemUrl: string;
constructor(protected injector: Injector, constructor(protected injector: Injector,
protected roleService: RoleService,
protected router: Router) { protected router: Router) {
super(ResourceType.Workspaceitem, injector, router); super(ResourceType.Workspaceitem, injector, router);
} }
ngOnInit() {
this.isAdmin = this.roleService.isAdmin();
this.itemUrl = this.object.firstMetadataValue('dc.identifier.uri');
}
initObjects(object: Item) { initObjects(object: Item) {
this.object = object; this.object = object;
} }

View File

@@ -4,10 +4,9 @@ import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-da
import { ClaimedTaskDataService } from '../../core/tasks/claimed-task-data.service'; import { ClaimedTaskDataService } from '../../core/tasks/claimed-task-data.service';
import { PoolTaskDataService } from '../../core/tasks/pool-task-data.service'; import { PoolTaskDataService } from '../../core/tasks/pool-task-data.service';
import { WorkflowitemDataService } from '../../core/submission/workflowitem-data.service'; import { WorkflowitemDataService } from '../../core/submission/workflowitem-data.service';
import { NormalizedObject } from '../../core/cache/models/normalized-object.model';
import { CacheableObject } from '../../core/cache/object-cache.reducer'; import { CacheableObject } from '../../core/cache/object-cache.reducer';
export class MydspaceActionsServiceFactory<T extends CacheableObject, TNormalized extends NormalizedObject, TService extends DataService<TNormalized, T>> { export class MydspaceActionsServiceFactory<T extends CacheableObject, TService extends DataService<T>> {
public getConstructor(type: ResourceType): TService { public getConstructor(type: ResourceType): TService {
switch (type) { switch (type) {
case ResourceType.Workspaceitem: { case ResourceType.Workspaceitem: {

View File

@@ -5,17 +5,16 @@ import { find } from 'rxjs/operators';
import { MydspaceActionsServiceFactory } from './mydspace-actions-service.factory'; import { MydspaceActionsServiceFactory } from './mydspace-actions-service.factory';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { NormalizedObject } from '../../core/cache/models/normalized-object.model';
import { DataService } from '../../core/data/data.service'; import { DataService } from '../../core/data/data.service';
import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { ResourceType } from '../../core/shared/resource-type'; import { ResourceType } from '../../core/shared/resource-type';
export abstract class MyDSpaceActionsComponent<T extends DSpaceObject, TNormalized extends NormalizedObject, TService extends DataService<TNormalized, T>> { export abstract class MyDSpaceActionsComponent<T extends DSpaceObject, TService extends DataService<T>> {
@Input() abstract object: T; @Input() abstract object: T;
protected objectDataService: TService; protected objectDataService: TService;
constructor(protected objectType: ResourceType, protected injector: Injector, protected router: Router) { constructor(protected objectType: ResourceType, protected injector: Injector, protected router: Router) {
const factory = new MydspaceActionsServiceFactory<T, TNormalized, TService>(); const factory = new MydspaceActionsServiceFactory<T, TService>();
this.objectDataService = injector.get(factory.getConstructor(objectType)); this.objectDataService = injector.get(factory.getConstructor(objectType));
} }

View File

@@ -5,8 +5,8 @@
ngbTooltip="{{'submission.workflow.tasks.pool.claim_help' | translate}}" ngbTooltip="{{'submission.workflow.tasks.pool.claim_help' | translate}}"
[disabled]="(processingClaim$ | async)" [disabled]="(processingClaim$ | async)"
(click)="claim()"> (click)="claim()">
<span *ngIf="(processingClaim$ | async)"><i class='fa fa-circle-o-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span> <span *ngIf="(processingClaim$ | async)"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
<span *ngIf="!(processingClaim$ | async)"><i class="fa fa-hand-paper-o"></i> {{'submission.workflow.tasks.pool.claim' | translate}}</span> <span *ngIf="!(processingClaim$ | async)"><i class="fas fa-hand-paper"></i> {{'submission.workflow.tasks.pool.claim' | translate}}</span>
</button> </button>
<ds-message-board <ds-message-board

View File

@@ -13,7 +13,6 @@ import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service'
import { NotificationsService } from '../../notifications/notifications.service'; import { NotificationsService } from '../../notifications/notifications.service';
import { NotificationOptions } from '../../notifications/models/notification-options.model'; import { NotificationOptions } from '../../notifications/models/notification-options.model';
import { isNotUndefined } from '../../empty.util'; import { isNotUndefined } from '../../empty.util';
import { NormalizedPoolTask } from '../../../core/tasks/models/normalized-pool-task-object.model';
import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { MyDSpaceActionsComponent } from '../mydspace-actions';
import { ResourceType } from '../../../core/shared/resource-type'; import { ResourceType } from '../../../core/shared/resource-type';
@@ -23,7 +22,7 @@ import { ResourceType } from '../../../core/shared/resource-type';
templateUrl: './pool-task-actions.component.html', templateUrl: './pool-task-actions.component.html',
}) })
export class PoolTaskActionsComponent extends MyDSpaceActionsComponent<PoolTask, NormalizedPoolTask, PoolTaskDataService> { export class PoolTaskActionsComponent extends MyDSpaceActionsComponent<PoolTask, PoolTaskDataService> {
@Input() object: PoolTask; @Input() object: PoolTask;
public processingClaim$ = new BehaviorSubject<boolean>(false); public processingClaim$ = new BehaviorSubject<boolean>(false);

View File

@@ -1,9 +1,9 @@
import { Component, Injector, Input } from '@angular/core'; import { Component, Injector, Input } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { MyDSpaceActionsComponent } from '../mydspace-actions';
import { ResourceType } from '../../../core/shared/resource-type'; import { ResourceType } from '../../../core/shared/resource-type';
import { Workflowitem } from '../../../core/submission/models/workflowitem.model'; import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
import { NormalizedWorkflowItem } from '../../../core/submission/models/normalized-workflowitem.model';
import { WorkflowitemDataService } from '../../../core/submission/workflowitem-data.service'; import { WorkflowitemDataService } from '../../../core/submission/workflowitem-data.service';
@Component({ @Component({
@@ -12,7 +12,7 @@ import { WorkflowitemDataService } from '../../../core/submission/workflowitem-d
templateUrl: './workflowitem-actions.component.html', templateUrl: './workflowitem-actions.component.html',
}) })
export class WorkflowitemActionsComponent extends MyDSpaceActionsComponent<Workflowitem, NormalizedWorkflowItem, WorkflowitemDataService> { export class WorkflowitemActionsComponent extends MyDSpaceActionsComponent<Workflowitem, WorkflowitemDataService> {
@Input() object: Workflowitem; @Input() object: Workflowitem;
constructor(protected injector: Injector, constructor(protected injector: Injector,

View File

@@ -9,7 +9,7 @@
class="btn btn-danger mt-1 mb-3" class="btn btn-danger mt-1 mb-3"
ngbTooltip="{{'submission.workflow.generic.delete-help' | translate}}" ngbTooltip="{{'submission.workflow.generic.delete-help' | translate}}"
(click)="$event.preventDefault();confirmDiscard(content)"> (click)="$event.preventDefault();confirmDiscard(content)">
<span *ngIf="(processingDelete$ | async)"><i class='fa fa-circle-o-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span> <span *ngIf="(processingDelete$ | async)"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
<span *ngIf="!(processingDelete$ | async)"><i class="fa fa-trash"></i> {{'submission.workflow.generic.delete' | translate}}</span> <span *ngIf="!(processingDelete$ | async)"><i class="fa fa-trash"></i> {{'submission.workflow.generic.delete' | translate}}</span>
</button> </button>

View File

@@ -7,7 +7,6 @@ import { TranslateService } from '@ngx-translate/core';
import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model'; import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model';
import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { MyDSpaceActionsComponent } from '../mydspace-actions';
import { NormalizedWorkspaceItem } from '../../../core/submission/models/normalized-workspaceitem.model';
import { SubmissionRestService } from '../../../submission/submission-rest.service'; import { SubmissionRestService } from '../../../submission/submission-rest.service';
import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service'; import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service';
import { ResourceType } from '../../../core/shared/resource-type'; import { ResourceType } from '../../../core/shared/resource-type';
@@ -20,7 +19,7 @@ import { NotificationOptions } from '../../notifications/models/notification-opt
templateUrl: './workspaceitem-actions.component.html', templateUrl: './workspaceitem-actions.component.html',
}) })
export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent<Workspaceitem, NormalizedWorkspaceItem, WorkspaceitemDataService> { export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent<Workspaceitem, WorkspaceitemDataService> {
@Input() object: Workspaceitem; @Input() object: Workspaceitem;
public processingDelete$ = new BehaviorSubject<boolean>(false); public processingDelete$ = new BehaviorSubject<boolean>(false);

View File

@@ -52,7 +52,6 @@ export class NotificationsService {
options: Partial<NotificationOptions> = {}, options: Partial<NotificationOptions> = {},
html: boolean = false): INotification { html: boolean = false): INotification {
const notificationOptions = { ...this.getDefaultOptions(), ...options }; const notificationOptions = { ...this.getDefaultOptions(), ...options };
console.log(notificationOptions);
const notification = new Notification(uniqueId(), NotificationType.Info, title, content, notificationOptions, html); const notification = new Notification(uniqueId(), NotificationType.Info, title, content, notificationOptions, html);
this.add(notification); this.add(notification);
return notification; return notification;

View File

@@ -4,7 +4,7 @@ import { MyDSpaceResult } from '../../../+my-dspace-page/my-dspace-result.model'
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import { ListableObject } from '../../object-collection/shared/listable-object.model'; import { ListableObject } from '../../object-collection/shared/listable-object.model';
import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { Metadata } from '../../../core/shared/metadata.model'; import { Metadata } from '../../../core/shared/metadata.utils';
@Component({ @Component({
selector: 'ds-my-dspace-result-detail-element', selector: 'ds-my-dspace-result-detail-element',

View File

@@ -34,11 +34,11 @@ export class WorkflowitemMyDSpaceResultDetailElementComponent extends MyDSpaceRe
this.initItem(this.dso.item as Observable<RemoteData<Item>>); this.initItem(this.dso.item as Observable<RemoteData<Item>>);
} }
initItem(itemObs: Observable<RemoteData<Item>>) { initItem(item$: Observable<RemoteData<Item>>) {
itemObs.pipe( item$.pipe(
find((rd: RemoteData<any>) => rd.hasSucceeded && isNotUndefined(rd.payload)) find((rd: RemoteData<Item>) => rd.hasSucceeded && isNotUndefined(rd.payload))
).subscribe((rd: RemoteData<any>) => { ).subscribe((rd: RemoteData<Item>) => {
this.item = rd.payload[0]; this.item = rd.payload;
}); });
} }

View File

@@ -34,11 +34,11 @@ export class WorkspaceitemMyDSpaceResultDetailElementComponent extends MyDSpaceR
this.initItem(this.dso.item as Observable<RemoteData<Item>>); this.initItem(this.dso.item as Observable<RemoteData<Item>>);
} }
initItem(itemObs: Observable<RemoteData<Item>>) { initItem(item$: Observable<RemoteData<Item>>) {
itemObs.pipe( item$.pipe(
find((rd: RemoteData<any>) => rd.hasSucceeded && isNotUndefined(rd.payload)) find((rd: RemoteData<Item>) => rd.hasSucceeded && isNotUndefined(rd.payload))
).subscribe((rd: RemoteData<any>) => { ).subscribe((rd: RemoteData<Item>) => {
this.item = rd.payload[0]; this.item = rd.payload;
}); });
} }
} }

View File

@@ -3,7 +3,7 @@ import { Component, Input } from '@angular/core';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { fadeInOut } from '../../animations/fade'; import { fadeInOut } from '../../animations/fade';
import { MyDspaceItemStatusType } from '../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { MyDspaceItemStatusType } from '../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
import { Metadata } from '../../../core/shared/metadata.model'; import { Metadata } from '../../../core/shared/metadata.utils';
@Component({ @Component({
selector: 'ds-item-list-preview', selector: 'ds-item-list-preview',

View File

@@ -4,8 +4,7 @@ import { MyDSpaceResult } from '../../../+my-dspace-page/my-dspace-result.model'
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import { ListableObject } from '../../object-collection/shared/listable-object.model'; import { ListableObject } from '../../object-collection/shared/listable-object.model';
import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { Metadata } from '../../../core/shared/metadata.model'; import { Metadata } from '../../../core/shared/metadata.utils';
import { TruncatableService } from '../../truncatable/truncatable.service';
@Component({ @Component({
selector: 'ds-my-dspace-result-list-element', selector: 'ds-my-dspace-result-list-element',

View File

@@ -8,7 +8,7 @@
<button type="button" <button type="button"
class="btn btn-primary mt-1 mb-3" class="btn btn-primary mt-1 mb-3"
(click)="view()"> (click)="view()">
<span>{{"mydspace.view-btn" | translate}}</span> <span><i class='fas fa-eye'></i> {{"mydspace.view-btn" | translate}}</span>
</button> </button>
</ds-pool-task-actions> </ds-pool-task-actions>

View File

@@ -1,5 +1,6 @@
<div class="btn-group" data-toggle="buttons"> <div class="btn-group" data-toggle="buttons">
<a routerLink="." <a *ngIf="isToShow(viewModeEnum.List)"
routerLink="."
[queryParams]="{view: 'list'}" [queryParams]="{view: 'list'}"
queryParamsHandling="merge" queryParamsHandling="merge"
(click)="switchViewTo(viewModeEnum.List)" (click)="switchViewTo(viewModeEnum.List)"
@@ -8,13 +9,24 @@
class="btn btn-secondary"> class="btn btn-secondary">
<i class="fas fa-list" title="{{'search.view-switch.show-list' | translate}}"></i> <i class="fas fa-list" title="{{'search.view-switch.show-list' | translate}}"></i>
</a> </a>
<a routerLink="." <a *ngIf="isToShow(viewModeEnum.Grid)"
routerLink="."
[queryParams]="{view: 'grid'}" [queryParams]="{view: 'grid'}"
queryParamsHandling="merge" queryParamsHandling="merge"
(click)="switchViewTo(viewModeEnum.Grid)" (click)="switchViewTo(viewModeEnum.Grid)"
routerLinkActive="active" routerLinkActive="active"
[class.active]="currentMode !== viewModeEnum.List" [class.active]="currentMode === viewModeEnum.Grid"
class="btn btn-secondary"> class="btn btn-secondary">
<i class="fas fa-th-large" title="{{'search.view-switch.show-grid' | translate}}"></i> <i class="fas fa-th-large" title="{{'search.view-switch.show-grid' | translate}}"></i>
</a> </a>
</div> <a *ngIf="isToShow(viewModeEnum.Detail)"
routerLink="."
[queryParams]="{view: 'detail'}"
queryParamsHandling="merge"
(click)="switchViewTo(viewModeEnum.Detail)"
routerLinkActive="active"
[class.active]="currentMode === viewModeEnum.Detail"
class="btn btn-secondary">
<i class="far fa-square" title="{{'search.view-switch.show-detail' | translate}}"></i>
</a>
</div>

View File

@@ -1,7 +1,10 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { SearchService } from './../../+search-page/search-service/search.service'; import { SearchService } from '../../+search-page/search-service/search.service';
import { ViewMode } from '../../core/shared/view-mode.model'; import { ViewMode } from '../../core/shared/view-mode.model';
import { isEmpty } from '../empty.util';
/** /**
* Component to switch between list and grid views. * Component to switch between list and grid views.
@@ -12,6 +15,8 @@ import { ViewMode } from '../../core/shared/view-mode.model';
templateUrl: './view-mode-switch.component.html' templateUrl: './view-mode-switch.component.html'
}) })
export class ViewModeSwitchComponent implements OnInit, OnDestroy { export class ViewModeSwitchComponent implements OnInit, OnDestroy {
@Input() viewModeList: ViewMode[];
currentMode: ViewMode = ViewMode.List; currentMode: ViewMode = ViewMode.List;
viewModeEnum = ViewMode; viewModeEnum = ViewMode;
private sub: Subscription; private sub: Subscription;
@@ -20,6 +25,10 @@ export class ViewModeSwitchComponent implements OnInit, OnDestroy {
} }
ngOnInit(): void { ngOnInit(): void {
if (isEmpty(this.viewModeList)) {
this.viewModeList = [ViewMode.List, ViewMode.Grid];
}
this.sub = this.searchService.getViewMode().subscribe((viewMode: ViewMode) => { this.sub = this.searchService.getViewMode().subscribe((viewMode: ViewMode) => {
this.currentMode = viewMode; this.currentMode = viewMode;
}); });
@@ -34,4 +43,8 @@ export class ViewModeSwitchComponent implements OnInit, OnDestroy {
this.sub.unsubscribe(); this.sub.unsubscribe();
} }
} }
isToShow(viewMode: ViewMode) {
return this.viewModeList && this.viewModeList.includes(viewMode);
}
} }