diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index b90a3edd46..f1b0dc4f37 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -164,6 +164,7 @@ import { CoreState } from './core-state.model'; import { GroupDataService } from './eperson/group-data.service'; import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model'; import { AccessStatusObject } from '../shared/object-list/access-status-badge/access-status.model'; +import { AccessStatusDataService } from './data/access-status-data.service'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -221,6 +222,7 @@ const PROVIDERS = [ MyDSpaceResponseParsingService, ServerResponseService, BrowseService, + AccessStatusDataService, SubmissionCcLicenseDataService, SubmissionCcLicenseUrlDataService, SubmissionFormsConfigService, diff --git a/src/app/core/data/access-status-data.service.ts b/src/app/core/data/access-status-data.service.ts new file mode 100644 index 0000000000..09843fac9b --- /dev/null +++ b/src/app/core/data/access-status-data.service.ts @@ -0,0 +1,45 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { dataService } from '../cache/builders/build-decorators'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { DataService } from './data.service'; +import { RequestService } from './request.service'; +import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; +import { CoreState } from '../core-state.model'; +import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; +import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type'; +import { Observable } from 'rxjs'; +import { RemoteData } from './remote-data'; +import { Item } from '../shared/item.model'; + +@Injectable() +@dataService(ACCESS_STATUS) +export class AccessStatusDataService extends DataService { + + protected linkPath = 'accessStatus'; + + constructor( + protected comparator: DefaultChangeAnalyzer, + protected halService: HALEndpointService, + protected http: HttpClient, + protected notificationsService: NotificationsService, + protected objectCache: ObjectCacheService, + protected rdbService: RemoteDataBuildService, + protected requestService: RequestService, + protected store: Store, + ) { + super(); + } + + /** + * Returns {@link RemoteData} of {@link AccessStatusObject} that is the access status of the given item + * @param item Item we want the access status of + */ + findAccessStatusFor(item: Item): Observable> { + return this.findByHref(item._links.accessStatus.href); + } +} diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts index c7f0f541f8..cb5d7a3d57 100644 --- a/src/app/core/data/item-data.service.ts +++ b/src/app/core/data/item-data.service.ts @@ -36,7 +36,6 @@ import { sendRequest } from '../shared/request.operators'; import { RestRequest } from './rest-request.model'; import { CoreState } from '../core-state.model'; import { FindListOptions } from './find-list-options.model'; -import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; @Injectable() @dataService(ITEM) @@ -292,33 +291,6 @@ export class ItemDataService extends DataService { ); } - /** - * Get the endpoint for an item's access status - * @param itemId - */ - public getAccessStatusEndpoint(itemId: string): Observable { - return this.halService.getEndpoint(this.linkPath).pipe( - switchMap((url: string) => this.halService.getEndpoint('accessStatus', `${url}/${itemId}`)) - ); - } - - /** - * Get the the access status - * @param itemId - */ - public getAccessStatus(itemId: string): Observable> { - const hrefObs = this.getAccessStatusEndpoint(itemId); - - hrefObs.pipe( - take(1) - ).subscribe((href) => { - const request = new GetRequest(this.requestService.generateRequestId(), href); - this.requestService.send(request); - }); - - return this.rdbService.buildSingle(hrefObs); - } - /** * Invalidate the cache of the item * @param itemUUID diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index d98c22225e..49ca7750b4 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -21,6 +21,8 @@ import { Version } from './version.model'; import { VERSION } from './version.resource-type'; import { BITSTREAM } from './bitstream.resource-type'; import { Bitstream } from './bitstream.model'; +import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type'; +import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; /** * Class representing a DSpace Item @@ -72,6 +74,7 @@ export class Item extends DSpaceObject implements ChildHALResource { templateItemOf: HALLink; version: HALLink; thumbnail: HALLink; + accessStatus: HALLink; self: HALLink; }; @@ -110,6 +113,13 @@ export class Item extends DSpaceObject implements ChildHALResource { @link(BITSTREAM, false, 'thumbnail') thumbnail?: Observable>; + /** + * The access status for this Item + * Will be undefined unless the access status {@link HALLink} has been resolved. + */ + @link(ACCESS_STATUS) + accessStatus?: Observable>; + /** * Method that returns as which type of object this object should be rendered */ diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html index 7ccc722526..4344cf9a00 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html @@ -18,7 +18,7 @@
- +

diff --git a/src/app/shared/object-list/access-status-badge/access-status-badge.component.ts b/src/app/shared/object-list/access-status-badge/access-status-badge.component.ts index 82f8fa3a03..59ac30f393 100644 --- a/src/app/shared/object-list/access-status-badge/access-status-badge.component.ts +++ b/src/app/shared/object-list/access-status-badge/access-status-badge.component.ts @@ -1,11 +1,11 @@ import { Component, Input } from '@angular/core'; import { catchError, map } from 'rxjs/operators'; import { Observable, of as observableOf } from 'rxjs'; -import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators'; -import { ItemDataService } from 'src/app/core/data/item-data.service'; import { AccessStatusObject } from './access-status.model'; import { hasValue } from '../../empty.util'; import { environment } from 'src/environments/environment'; +import { Item } from 'src/app/core/shared/item.model'; +import { AccessStatusDataService } from 'src/app/core/data/access-status-data.service'; @Component({ selector: 'ds-access-status-badge', @@ -16,7 +16,7 @@ import { environment } from 'src/environments/environment'; */ export class AccessStatusBadgeComponent { - @Input() uuid: string; + @Input() item: Item; accessStatus$: Observable; /** @@ -27,26 +27,31 @@ export class AccessStatusBadgeComponent { /** * Initialize instance variables * - * @param {ItemDataService} itemDataService + * @param {AccessStatusDataService} accessStatusDataService */ - constructor(private itemDataService: ItemDataService) { } + constructor(private accessStatusDataService: AccessStatusDataService) { } ngOnInit(): void { this.showAccessStatus = environment.item.showAccessStatuses; - this.accessStatus$ = this.itemDataService - .getAccessStatus(this.uuid) - .pipe( - getFirstCompletedRemoteData(), - map((accessStatusRD) => { - if (accessStatusRD.statusCode !== 401 && hasValue(accessStatusRD.payload)) { - return accessStatusRD.payload; - } else { - return []; - } - }), - map((accessStatus: AccessStatusObject) => hasValue(accessStatus.status) ? accessStatus.status : 'unknown'), - map((status: string) => `access-status.${status.toLowerCase()}.listelement.badge`), - catchError(() => observableOf('access-status.unknown.listelement.badge')) - ); + if (!this.showAccessStatus || this.item == null) { + // Do not show the badge if the feature is inactive or if the item is null. + return; + } + if (this.item.accessStatus == null) { + // In case the access status has not been loaded, do it individually. + this.item.accessStatus = this.accessStatusDataService.findAccessStatusFor(this.item); + } + this.accessStatus$ = this.item.accessStatus.pipe( + map((accessStatusRD) => { + if (accessStatusRD.statusCode !== 401 && hasValue(accessStatusRD.payload)) { + return accessStatusRD.payload; + } else { + return []; + } + }), + map((accessStatus: AccessStatusObject) => hasValue(accessStatus.status) ? accessStatus.status : 'unknown'), + map((status: string) => `access-status.${status.toLowerCase()}.listelement.badge`), + catchError(() => observableOf('access-status.unknown.listelement.badge')) + ); } } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html index 137b59d77b..6d4c704b60 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html @@ -4,7 +4,7 @@
- +

diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html index b51c45290b..645cace8dc 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html @@ -1,6 +1,6 @@
- +
diff --git a/src/app/shared/search/search.component.ts b/src/app/shared/search/search.component.ts index e660b4bd3c..4d8a7c24df 100644 --- a/src/app/shared/search/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -31,6 +31,7 @@ import { ViewMode } from '../../core/shared/view-mode.model'; import { SelectionConfig } from './search-results/search-results.component'; import { ListableObject } from '../object-collection/shared/listable-object.model'; import { CollectionElementLinkType } from '../object-collection/collection-element-link.type'; +import { environment } from 'src/environments/environment'; @Component({ selector: 'ds-search', @@ -355,7 +356,8 @@ export class SearchComponent implements OnInit { undefined, this.useCachedVersionIfAvailable, true, - followLink('thumbnail', { isOptional: true }) + followLink('thumbnail', { isOptional: true }), + followLink('accessStatus', { isOptional: true, shouldEmbed: environment.item.showAccessStatuses }) ).pipe(getFirstCompletedRemoteData()) .subscribe((results: RemoteData>) => { if (results.hasSucceeded && results.payload?.page?.length > 0) {