mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge pull request #1261 from atmire/w2p-80369_Use-thumbnail-links-for-Items-and-Bitstreams
Use thumbnail links for items and bitstreams
This commit is contained in:
@@ -35,7 +35,7 @@ export class BitstreamPageResolver implements Resolve<RemoteData<Bitstream>> {
|
||||
*/
|
||||
get followLinks(): FollowLinkConfig<Bitstream>[] {
|
||||
return [
|
||||
followLink('bundle', undefined, true, true, true, followLink('item')),
|
||||
followLink('bundle', {}, followLink('item')),
|
||||
followLink('format')
|
||||
];
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ import { ResolvedAction } from '../core/resolving/resolver.actions';
|
||||
* Requesting them as embeds will limit the number of requests
|
||||
*/
|
||||
export const COLLECTION_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Collection>[] = [
|
||||
followLink('parentCommunity', undefined, true, true, true,
|
||||
followLink('parentCommunity', {},
|
||||
followLink('parentCommunity')
|
||||
),
|
||||
followLink('logo')
|
||||
|
@@ -4,10 +4,9 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
|
||||
import { catchError, filter, first, map, mergeMap, take } from 'rxjs/operators';
|
||||
|
||||
import { PaginatedList, buildPaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import {
|
||||
getFirstSucceededRemoteDataPayload,
|
||||
getFirstSucceededRemoteDataWithNotEmptyPayload
|
||||
getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteDataWithNotEmptyPayload,
|
||||
} from '../../../core/shared/operators';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||
@@ -15,7 +14,6 @@ import { LinkService } from '../../../core/cache/builders/link.service';
|
||||
import { Bundle } from '../../../core/shared/bundle.model';
|
||||
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
||||
import { Bitstream } from '../../../core/shared/bitstream.model';
|
||||
import { FindListOptions } from '../../../core/data/request.models';
|
||||
|
||||
/**
|
||||
* Interface for a bundle's bitstream map entry
|
||||
@@ -79,7 +77,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
||||
getFirstSucceededRemoteDataWithNotEmptyPayload(),
|
||||
map((item: Item) => this.linkService.resolveLink(
|
||||
item,
|
||||
followLink('bundles', new FindListOptions(), true, true, true, followLink('bitstreams'))
|
||||
followLink('bundles', {}, followLink('bitstreams'))
|
||||
))
|
||||
) as Observable<Item>;
|
||||
|
||||
|
@@ -68,7 +68,8 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
|
||||
{elementsPerPage: options.pageSize, currentPage: options.currentPage},
|
||||
true,
|
||||
true,
|
||||
followLink('format')
|
||||
followLink('format'),
|
||||
followLink('thumbnail'),
|
||||
)),
|
||||
tap((rd: RemoteData<PaginatedList<Bitstream>>) => {
|
||||
if (hasValue(rd.errorMessage)) {
|
||||
@@ -85,7 +86,8 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
|
||||
{elementsPerPage: options.pageSize, currentPage: options.currentPage},
|
||||
true,
|
||||
true,
|
||||
followLink('format')
|
||||
followLink('format'),
|
||||
followLink('thumbnail'),
|
||||
)),
|
||||
tap((rd: RemoteData<PaginatedList<Bitstream>>) => {
|
||||
if (hasValue(rd.errorMessage)) {
|
||||
|
@@ -5,7 +5,6 @@ import { RemoteData } from '../core/data/remote-data';
|
||||
import { ItemDataService } from '../core/data/item-data.service';
|
||||
import { Item } from '../core/shared/item.model';
|
||||
import { followLink, FollowLinkConfig } from '../shared/utils/follow-link-config.model';
|
||||
import { FindListOptions } from '../core/data/request.models';
|
||||
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { ResolvedAction } from '../core/resolving/resolver.actions';
|
||||
@@ -15,13 +14,13 @@ import { ResolvedAction } from '../core/resolving/resolver.actions';
|
||||
* Requesting them as embeds will limit the number of requests
|
||||
*/
|
||||
export const ITEM_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Item>[] = [
|
||||
followLink('owningCollection', undefined, true, true, true,
|
||||
followLink('parentCommunity', undefined, true, true, true,
|
||||
followLink('owningCollection', {},
|
||||
followLink('parentCommunity', {},
|
||||
followLink('parentCommunity'))
|
||||
),
|
||||
followLink('bundles', new FindListOptions(), true, true, true, followLink('bitstreams')),
|
||||
followLink('relationships'),
|
||||
followLink('version', undefined, true, true, true, followLink('versionhistory')),
|
||||
followLink('version', {}, followLink('versionhistory')),
|
||||
followLink('thumbnail')
|
||||
];
|
||||
|
||||
/**
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ng-container *ngIf="!mediaViewer.image">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="thumbnail$ | async"></ds-thumbnail>
|
||||
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
|
||||
</ds-metadata-field-wrapper>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="mediaViewer.image">
|
||||
|
@@ -1,12 +1,7 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { environment } from '../../../../../environments/environment';
|
||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { takeUntilCompletedRemoteData } from '../../../../core/shared/operators';
|
||||
import { getItemPageRoute } from '../../../item-page-routing-paths';
|
||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item',
|
||||
@@ -18,28 +13,14 @@ import { RemoteData } from '../../../../core/data/remote-data';
|
||||
export class ItemComponent implements OnInit {
|
||||
@Input() object: Item;
|
||||
|
||||
/**
|
||||
* The Item's thumbnail
|
||||
*/
|
||||
thumbnail$: BehaviorSubject<RemoteData<Bitstream>>;
|
||||
|
||||
/**
|
||||
* Route to the item page
|
||||
*/
|
||||
itemPageRoute: string;
|
||||
mediaViewer = environment.mediaViewer;
|
||||
|
||||
constructor(protected bitstreamDataService: BitstreamDataService) {
|
||||
}
|
||||
mediaViewer = environment.mediaViewer;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.itemPageRoute = getItemPageRoute(this.object);
|
||||
|
||||
this.thumbnail$ = new BehaviorSubject<RemoteData<Bitstream>>(undefined);
|
||||
this.bitstreamDataService.getThumbnailFor(this.object).pipe(
|
||||
takeUntilCompletedRemoteData(),
|
||||
).subscribe((rd: RemoteData<Bitstream>) => {
|
||||
this.thumbnail$.next(rd);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ng-container *ngIf="!mediaViewer.image">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="thumbnail$ | async"></ds-thumbnail>
|
||||
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
|
||||
</ds-metadata-field-wrapper>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="mediaViewer.image">
|
||||
|
@@ -19,6 +19,8 @@ import { currentPath } from '../shared/utils/route.utils';
|
||||
import { Router } from '@angular/router';
|
||||
import { Context } from '../core/shared/context.model';
|
||||
import { SortOptions } from '../core/cache/models/sort-options.model';
|
||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||
import { Item } from '../core/shared/item.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-search',
|
||||
@@ -128,8 +130,11 @@ export class SearchComponent implements OnInit {
|
||||
this.searchLink = this.getSearchLink();
|
||||
this.searchOptions$ = this.getSearchOptions();
|
||||
this.sub = this.searchOptions$.pipe(
|
||||
switchMap((options) => this.service.search(options).pipe(getFirstSucceededRemoteData(), startWith(undefined))))
|
||||
.subscribe((results) => {
|
||||
switchMap((options) => this.service.search(
|
||||
options, undefined, true, true, followLink<Item>('thumbnail', { isOptional: true })
|
||||
).pipe(getFirstSucceededRemoteData(), startWith(undefined))
|
||||
)
|
||||
).subscribe((results) => {
|
||||
this.resultsRD$.next(results);
|
||||
});
|
||||
this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe(
|
||||
|
@@ -174,8 +174,8 @@ export class CommunityListService {
|
||||
direction: options.sort.direction
|
||||
}
|
||||
},
|
||||
followLink('subcommunities', this.configOnePage, true, true),
|
||||
followLink('collections', this.configOnePage, true, true))
|
||||
followLink('subcommunities', { findListOptions: this.configOnePage }),
|
||||
followLink('collections', { findListOptions: this.configOnePage }))
|
||||
.pipe(
|
||||
getFirstSucceededRemoteData(),
|
||||
map((results) => results.payload),
|
||||
@@ -242,8 +242,8 @@ export class CommunityListService {
|
||||
elementsPerPage: MAX_COMCOLS_PER_PAGE,
|
||||
currentPage: i
|
||||
},
|
||||
followLink('subcommunities', this.configOnePage, true, true),
|
||||
followLink('collections', this.configOnePage, true, true))
|
||||
followLink('subcommunities', { findListOptions: this.configOnePage }),
|
||||
followLink('collections', { findListOptions: this.configOnePage }))
|
||||
.pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
switchMap((rd: RemoteData<PaginatedList<Community>>) => {
|
||||
|
10
src/app/core/cache/builders/link.service.spec.ts
vendored
10
src/app/core/cache/builders/link.service.spec.ts
vendored
@@ -102,7 +102,7 @@ describe('LinkService', () => {
|
||||
describe('resolveLink', () => {
|
||||
describe(`when the linkdefinition concerns a single object`, () => {
|
||||
beforeEach(() => {
|
||||
service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor')));
|
||||
service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')));
|
||||
});
|
||||
it('should call dataservice.findByHref with the correct href and nested links', () => {
|
||||
expect(testDataService.findByHref).toHaveBeenCalledWith(testModel._links.predecessor.href, true, true, followLink('successor'));
|
||||
@@ -116,7 +116,7 @@ describe('LinkService', () => {
|
||||
propertyName: 'predecessor',
|
||||
isList: true
|
||||
});
|
||||
service.resolveLink(testModel, followLink('predecessor', { some: 'options ' } as any, true, true, true, followLink('successor')));
|
||||
service.resolveLink(testModel, followLink('predecessor', { findListOptions: { some: 'options ' } as any }, followLink('successor')));
|
||||
});
|
||||
it('should call dataservice.findAllByHref with the correct href, findListOptions, and nested links', () => {
|
||||
expect(testDataService.findAllByHref).toHaveBeenCalledWith(testModel._links.predecessor.href, { some: 'options ' } as any, true, true, followLink('successor'));
|
||||
@@ -124,7 +124,7 @@ describe('LinkService', () => {
|
||||
});
|
||||
describe('either way', () => {
|
||||
beforeEach(() => {
|
||||
result = service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor')));
|
||||
result = service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')));
|
||||
});
|
||||
|
||||
it('should call getLinkDefinition with the correct model and link', () => {
|
||||
@@ -149,7 +149,7 @@ describe('LinkService', () => {
|
||||
});
|
||||
it('should throw an error', () => {
|
||||
expect(() => {
|
||||
service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor')));
|
||||
service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')));
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -160,7 +160,7 @@ describe('LinkService', () => {
|
||||
});
|
||||
it('should throw an error', () => {
|
||||
expect(() => {
|
||||
service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor')));
|
||||
service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')));
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
7
src/app/core/cache/builders/link.service.ts
vendored
7
src/app/core/cache/builders/link.service.ts
vendored
@@ -55,9 +55,7 @@ export class LinkService {
|
||||
public resolveLinkWithoutAttaching<T extends HALResource, U extends HALResource>(model, linkToFollow: FollowLinkConfig<T>): Observable<RemoteData<U>> {
|
||||
const matchingLinkDef = this.getLinkDefinition(model.constructor, linkToFollow.name);
|
||||
|
||||
if (hasNoValue(matchingLinkDef)) {
|
||||
throw new Error(`followLink('${linkToFollow.name}') was used for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`);
|
||||
} else {
|
||||
if (hasValue(matchingLinkDef)) {
|
||||
const provider = this.getDataServiceFor(matchingLinkDef.resourceType);
|
||||
|
||||
if (hasNoValue(provider)) {
|
||||
@@ -84,7 +82,10 @@ export class LinkService {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else if (!linkToFollow.isOptional) {
|
||||
throw new Error(`followLink('${linkToFollow.name}') was used as a required link for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`);
|
||||
}
|
||||
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
|
@@ -523,7 +523,7 @@ describe('RemoteDataBuildService', () => {
|
||||
let paginatedLinksToFollow;
|
||||
beforeEach(() => {
|
||||
paginatedLinksToFollow = [
|
||||
followLink('page', undefined, true, true, true, ...linksToFollow),
|
||||
followLink('page', {}, ...linksToFollow),
|
||||
...linksToFollow
|
||||
];
|
||||
});
|
||||
|
@@ -271,7 +271,7 @@ export class RemoteDataBuildService {
|
||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||
*/
|
||||
buildList<T extends HALResource>(href$: string | Observable<string>, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
|
||||
return this.buildFromHref<PaginatedList<T>>(href$, followLink('page', undefined, false, true, true, ...linksToFollow));
|
||||
return this.buildFromHref<PaginatedList<T>>(href$, followLink('page', { shouldEmbed: false }, ...linksToFollow));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||
import { map, switchMap, take } from 'rxjs/operators';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
@@ -18,7 +18,7 @@ import { Item } from '../shared/item.model';
|
||||
import { BundleDataService } from './bundle-data.service';
|
||||
import { DataService } from './data.service';
|
||||
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
|
||||
import { PaginatedList, buildPaginatedList } from './paginated-list.model';
|
||||
import { buildPaginatedList, PaginatedList } from './paginated-list.model';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { FindListOptions, PutRequest } from './request.models';
|
||||
import { RequestService } from './request.service';
|
||||
@@ -28,7 +28,6 @@ import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||
import { sendRequest } from '../shared/operators';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||
import { PageInfo } from '../shared/page-info.model';
|
||||
import { RequestEntryState } from './request.reducer';
|
||||
|
||||
/**
|
||||
* A service to retrieve {@link Bitstream}s from the REST API
|
||||
@@ -75,92 +74,6 @@ export class BitstreamDataService extends DataService<Bitstream> {
|
||||
return this.findAllByHref(bundle._links.bitstreams.href, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the thumbnail for the given item
|
||||
* @returns {Observable<RemoteData<{@link Bitstream}>>} the first bitstream in the THUMBNAIL bundle
|
||||
*/
|
||||
// TODO should be implemented rest side. {@link Item} should get a thumbnail link
|
||||
public getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
|
||||
return this.bundleService.findByItemAndName(item, 'THUMBNAIL').pipe(
|
||||
switchMap((bundleRD: RemoteData<Bundle>) => {
|
||||
if (isNotEmpty(bundleRD.payload)) {
|
||||
return this.findAllByBundle(bundleRD.payload, { elementsPerPage: 1 }).pipe(
|
||||
map((bitstreamRD: RemoteData<PaginatedList<Bitstream>>) => {
|
||||
if (hasValue(bitstreamRD.payload) && hasValue(bitstreamRD.payload.page)) {
|
||||
return new RemoteData(
|
||||
bitstreamRD.timeCompleted,
|
||||
bitstreamRD.msToLive,
|
||||
bitstreamRD.lastUpdated,
|
||||
bitstreamRD.state,
|
||||
bitstreamRD.errorMessage,
|
||||
bitstreamRD.payload.page[0],
|
||||
bitstreamRD.statusCode
|
||||
);
|
||||
} else {
|
||||
return bitstreamRD as any;
|
||||
}
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return [bundleRD as any];
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the matching thumbnail for a {@link Bitstream}.
|
||||
*
|
||||
* The {@link Item} is technically redundant, but is available
|
||||
* in all current use cases, and having it simplifies this method
|
||||
*
|
||||
* @param item The {@link Item} the {@link Bitstream} and its thumbnail are a part of
|
||||
* @param bitstreamInOriginal The original {@link Bitstream} to find the thumbnail for
|
||||
*/
|
||||
// TODO should be implemented rest side
|
||||
public getMatchingThumbnail(item: Item, bitstreamInOriginal: Bitstream): Observable<RemoteData<Bitstream>> {
|
||||
return this.bundleService.findByItemAndName(item, 'THUMBNAIL').pipe(
|
||||
switchMap((bundleRD: RemoteData<Bundle>) => {
|
||||
if (isNotEmpty(bundleRD.payload)) {
|
||||
return this.findAllByBundle(bundleRD.payload, { elementsPerPage: 9999 }).pipe(
|
||||
map((bitstreamRD: RemoteData<PaginatedList<Bitstream>>) => {
|
||||
if (hasValue(bitstreamRD.payload) && hasValue(bitstreamRD.payload.page)) {
|
||||
const matchingThumbnail = bitstreamRD.payload.page.find((thumbnail: Bitstream) =>
|
||||
thumbnail.name.startsWith(bitstreamInOriginal.name)
|
||||
);
|
||||
if (hasValue(matchingThumbnail)) {
|
||||
return new RemoteData(
|
||||
bitstreamRD.timeCompleted,
|
||||
bitstreamRD.msToLive,
|
||||
bitstreamRD.lastUpdated,
|
||||
bitstreamRD.state,
|
||||
bitstreamRD.errorMessage,
|
||||
matchingThumbnail,
|
||||
bitstreamRD.statusCode
|
||||
);
|
||||
} else {
|
||||
return new RemoteData(
|
||||
bitstreamRD.timeCompleted,
|
||||
bitstreamRD.msToLive,
|
||||
bitstreamRD.lastUpdated,
|
||||
RequestEntryState.Error,
|
||||
'No matching thumbnail found',
|
||||
undefined,
|
||||
404
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return bitstreamRD as any;
|
||||
}
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return [bundleRD as any];
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all {@link Bitstream}s in a certain {@link Bundle}.
|
||||
*
|
||||
|
@@ -233,7 +233,7 @@ describe('DataService', () => {
|
||||
const config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
elementsPerPage: 5
|
||||
});
|
||||
(service as any).getFindAllHref({}, null, followLink('bundles', config, true, true, true)).subscribe((value) => {
|
||||
(service as any).getFindAllHref({}, null, followLink('bundles', { findListOptions: config })).subscribe((value) => {
|
||||
expect(value).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -253,7 +253,7 @@ describe('DataService', () => {
|
||||
elementsPerPage: 2
|
||||
});
|
||||
|
||||
(service as any).getFindAllHref({}, null, followLink('bundles'), followLink('owningCollection', config, true, true, true), followLink('templateItemOf')).subscribe((value) => {
|
||||
(service as any).getFindAllHref({}, null, followLink('bundles'), followLink('owningCollection', { findListOptions: config }), followLink('templateItemOf')).subscribe((value) => {
|
||||
expect(value).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -261,7 +261,13 @@ describe('DataService', () => {
|
||||
it('should not include linksToFollow with shouldEmbed = false', () => {
|
||||
const expected = `${endpoint}?embed=templateItemOf`;
|
||||
|
||||
(service as any).getFindAllHref({}, null, followLink('bundles', undefined, false), followLink('owningCollection', undefined, false), followLink('templateItemOf')).subscribe((value) => {
|
||||
(service as any).getFindAllHref(
|
||||
{},
|
||||
null,
|
||||
followLink('bundles', { shouldEmbed: false }),
|
||||
followLink('owningCollection', { shouldEmbed: false }),
|
||||
followLink('templateItemOf')
|
||||
).subscribe((value) => {
|
||||
expect(value).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -269,7 +275,7 @@ describe('DataService', () => {
|
||||
it('should include nested linksToFollow 3lvl', () => {
|
||||
const expected = `${endpoint}?embed=owningCollection/itemtemplate/relationships`;
|
||||
|
||||
(service as any).getFindAllHref({}, null, followLink('owningCollection', undefined, true, true, true, followLink('itemtemplate', undefined, true, true, true, followLink('relationships')))).subscribe((value) => {
|
||||
(service as any).getFindAllHref({}, null, followLink('owningCollection', {}, followLink('itemtemplate', {}, followLink('relationships')))).subscribe((value) => {
|
||||
expect(value).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -279,7 +285,7 @@ describe('DataService', () => {
|
||||
const config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
elementsPerPage: 4
|
||||
});
|
||||
(service as any).getFindAllHref({}, null, followLink('owningCollection', undefined, true, true, true, followLink('itemtemplate', config, true, true, true))).subscribe((value) => {
|
||||
(service as any).getFindAllHref({}, null, followLink('owningCollection', {}, followLink('itemtemplate', { findListOptions: config }))).subscribe((value) => {
|
||||
expect(value).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -308,13 +314,19 @@ describe('DataService', () => {
|
||||
|
||||
it('should not include linksToFollow with shouldEmbed = false', () => {
|
||||
const expected = `${endpointMock}/${resourceIdMock}?embed=templateItemOf`;
|
||||
const result = (service as any).getIDHref(endpointMock, resourceIdMock, followLink('bundles', undefined, false), followLink('owningCollection', undefined, false), followLink('templateItemOf'));
|
||||
const result = (service as any).getIDHref(
|
||||
endpointMock,
|
||||
resourceIdMock,
|
||||
followLink('bundles', { shouldEmbed: false }),
|
||||
followLink('owningCollection', { shouldEmbed: false }),
|
||||
followLink('templateItemOf')
|
||||
);
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should include nested linksToFollow 3lvl', () => {
|
||||
const expected = `${endpointMock}/${resourceIdMock}?embed=owningCollection/itemtemplate/relationships`;
|
||||
const result = (service as any).getIDHref(endpointMock, resourceIdMock, followLink('owningCollection', undefined, true, true, true,followLink('itemtemplate', undefined, true, true, true, followLink('relationships'))));
|
||||
const result = (service as any).getIDHref(endpointMock, resourceIdMock, followLink('owningCollection', {}, followLink('itemtemplate', {}, followLink('relationships'))));
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
@@ -174,13 +174,29 @@ describe('DsoRedirectDataService', () => {
|
||||
|
||||
it('should not include linksToFollow with shouldEmbed = false', () => {
|
||||
const expected = `${requestUUIDURL}&embed=templateItemOf`;
|
||||
const result = (service as any).getIDHref(pidLink, dsoUUID, followLink('bundles', undefined, false), followLink('owningCollection', undefined, false), followLink('templateItemOf'));
|
||||
const result = (service as any).getIDHref(
|
||||
pidLink,
|
||||
dsoUUID,
|
||||
followLink('bundles', { shouldEmbed: false }),
|
||||
followLink('owningCollection', { shouldEmbed: false }),
|
||||
followLink('templateItemOf')
|
||||
);
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should include nested linksToFollow 3lvl', () => {
|
||||
const expected = `${requestUUIDURL}&embed=owningCollection/itemtemplate/relationships`;
|
||||
const result = (service as any).getIDHref(pidLink, dsoUUID, followLink('owningCollection', undefined, true, true, true, followLink('itemtemplate', undefined, true, true, true, followLink('relationships'))));
|
||||
const result = (service as any).getIDHref(
|
||||
pidLink,
|
||||
dsoUUID,
|
||||
followLink('owningCollection',
|
||||
{},
|
||||
followLink('itemtemplate',
|
||||
{},
|
||||
followLink('relationships')
|
||||
)
|
||||
)
|
||||
);
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
@@ -43,13 +43,14 @@ export class Bitstream extends DSpaceObject implements HALResource {
|
||||
bundle: HALLink;
|
||||
format: HALLink;
|
||||
content: HALLink;
|
||||
thumbnail: HALLink;
|
||||
};
|
||||
|
||||
/**
|
||||
* The thumbnail for this Bitstream
|
||||
* Needs to be resolved first, but isn't available as a {@link HALLink} yet
|
||||
* Use BitstreamDataService.getThumbnailFor(…) for now.
|
||||
* Will be undefined unless the thumbnail {@link HALLink} has been resolved.
|
||||
*/
|
||||
@link(BITSTREAM, false, 'thumbnail')
|
||||
thumbnail?: Observable<RemoteData<Bitstream>>;
|
||||
|
||||
/**
|
||||
|
@@ -19,6 +19,8 @@ import { ITEM } from './item.resource-type';
|
||||
import { ChildHALResource } from './child-hal-resource.model';
|
||||
import { Version } from './version.model';
|
||||
import { VERSION } from './version.resource-type';
|
||||
import { BITSTREAM } from './bitstream.resource-type';
|
||||
import { Bitstream } from './bitstream.model';
|
||||
|
||||
/**
|
||||
* Class representing a DSpace Item
|
||||
@@ -69,6 +71,7 @@ export class Item extends DSpaceObject implements ChildHALResource {
|
||||
owningCollection: HALLink;
|
||||
templateItemOf: HALLink;
|
||||
version: HALLink;
|
||||
thumbnail: HALLink;
|
||||
self: HALLink;
|
||||
};
|
||||
|
||||
@@ -100,6 +103,13 @@ export class Item extends DSpaceObject implements ChildHALResource {
|
||||
@link(RELATIONSHIP, true)
|
||||
relationships?: Observable<RemoteData<PaginatedList<Relationship>>>;
|
||||
|
||||
/**
|
||||
* The thumbnail for this Item
|
||||
* Will be undefined unless the thumbnail {@link HALLink} has been resolved.
|
||||
*/
|
||||
@link(BITSTREAM, false, 'thumbnail')
|
||||
thumbnail?: Observable<RemoteData<Bitstream>>;
|
||||
|
||||
/**
|
||||
* Method that returns as which type of object this object should be rendered
|
||||
*/
|
||||
|
@@ -41,6 +41,43 @@ import { SearchConfig } from './search-filters/search-config.model';
|
||||
import { PaginationService } from '../../pagination/pagination.service';
|
||||
import { SearchConfigurationService } from './search-configuration.service';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { DataService } from '../../data/data.service';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { CoreState } from '../../core.reducers';
|
||||
import { ObjectCacheService } from '../../cache/object-cache.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { DSOChangeAnalyzer } from '../../data/dso-change-analyzer.service';
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
/**
|
||||
* A class that lets us delegate some methods to DataService
|
||||
*/
|
||||
class DataServiceImpl extends DataService<any> {
|
||||
protected linkPath = 'discover';
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DSOChangeAnalyzer<any>) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the embed options to the link for the request
|
||||
* @param href The href the params are to be added to
|
||||
* @param args params for the query string
|
||||
* @param linksToFollow links we want to embed in query string if shouldEmbed is true
|
||||
*/
|
||||
public addEmbedParams(href: string, args: string[], ...linksToFollow: FollowLinkConfig<any>[]) {
|
||||
return super.addEmbedParams(href, args, ...linksToFollow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Service that performs all general actions that have to do with the search page
|
||||
@@ -78,6 +115,11 @@ export class SearchService implements OnDestroy {
|
||||
*/
|
||||
private sub;
|
||||
|
||||
/**
|
||||
* Instance of DataServiceImpl that lets us delegate some methods to DataService
|
||||
*/
|
||||
private searchDataService: DataServiceImpl;
|
||||
|
||||
constructor(private router: Router,
|
||||
private routeService: RouteService,
|
||||
protected requestService: RequestService,
|
||||
@@ -89,6 +131,16 @@ export class SearchService implements OnDestroy {
|
||||
private paginationService: PaginationService,
|
||||
private searchConfigurationService: SearchConfigurationService
|
||||
) {
|
||||
this.searchDataService = new DataServiceImpl(
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,7 +183,17 @@ export class SearchService implements OnDestroy {
|
||||
search<T extends DSpaceObject>(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<SearchObjects<T>>> {
|
||||
const href$ = this.getEndpoint(searchOptions);
|
||||
|
||||
href$.pipe(take(1)).subscribe((url: string) => {
|
||||
href$.pipe(
|
||||
take(1),
|
||||
map((href: string) => {
|
||||
const args = this.searchDataService.addEmbedParams(href, [], ...linksToFollow);
|
||||
if (isNotEmpty(args)) {
|
||||
return new URLCombiner(href, `?${args.join('&')}`).toString();
|
||||
} else {
|
||||
return href;
|
||||
}
|
||||
})
|
||||
).subscribe((url: string) => {
|
||||
const request = new this.request(this.requestService.generateRequestId(), url);
|
||||
|
||||
const getResponseParserFn: () => GenericConstructor<ResponseParsingService> = () => {
|
||||
|
@@ -173,7 +173,7 @@ export class VocabularyService {
|
||||
);
|
||||
|
||||
// TODO remove false for the entries embed when https://github.com/DSpace/DSpace/issues/3096 is solved
|
||||
return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', options, false)).pipe(
|
||||
return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', { findListOptions: options, shouldEmbed: false })).pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
switchMap((vocabulary: Vocabulary) => vocabulary.entries),
|
||||
);
|
||||
@@ -200,7 +200,7 @@ export class VocabularyService {
|
||||
);
|
||||
|
||||
// TODO remove false for the entries embed when https://github.com/DSpace/DSpace/issues/3096 is solved
|
||||
return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', options, false)).pipe(
|
||||
return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', { findListOptions: options, shouldEmbed: false })).pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
switchMap((vocabulary: Vocabulary) => vocabulary.entries),
|
||||
);
|
||||
@@ -249,7 +249,7 @@ export class VocabularyService {
|
||||
);
|
||||
|
||||
// TODO remove false for the entries embed when https://github.com/DSpace/DSpace/issues/3096 is solved
|
||||
return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', options, false)).pipe(
|
||||
return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', { findListOptions: options, shouldEmbed: false })).pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
switchMap((vocabulary: Vocabulary) => vocabulary.entries),
|
||||
getFirstSucceededRemoteListPayload(),
|
||||
|
@@ -8,13 +8,13 @@
|
||||
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
|
||||
class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</a>
|
||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</span>
|
||||
|
@@ -8,13 +8,13 @@
|
||||
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
|
||||
class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</a>
|
||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</span>
|
||||
|
@@ -8,13 +8,13 @@
|
||||
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
|
||||
class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</a>
|
||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</span>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="thumbnail$ | async"></ds-thumbnail>
|
||||
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
|
||||
</ds-metadata-field-wrapper>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['publicationvolume.volumeNumber']"
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="thumbnail$ | async"></ds-thumbnail>
|
||||
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
|
||||
</ds-metadata-field-wrapper>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['publicationvolume.volumeNumber']"
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="thumbnail$ | async"></ds-thumbnail>
|
||||
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
|
||||
</ds-metadata-field-wrapper>
|
||||
<ds-generic-item-page-field class="item-page-fields" [item]="object"
|
||||
[fields]="['creativeworkseries.issn']"
|
||||
|
@@ -8,13 +8,13 @@
|
||||
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
|
||||
class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</a>
|
||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</span>
|
||||
|
@@ -8,13 +8,13 @@
|
||||
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
|
||||
class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</a>
|
||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</span>
|
||||
|
@@ -8,13 +8,13 @@
|
||||
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
|
||||
class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</a>
|
||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</span>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="thumbnail$ | async"
|
||||
<ds-thumbnail [thumbnail]="object?.thumbnail | async"
|
||||
[defaultImage]="'assets/images/orgunit-placeholder.svg'"
|
||||
[alt]="'thumbnail.orgunit.alt'"
|
||||
[placeholder]="'thumbnail.orgunit.placeholder'"
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="thumbnail$ | async"
|
||||
<ds-thumbnail [thumbnail]="object?.thumbnail | async"
|
||||
[defaultImage]="'assets/images/person-placeholder.svg'"
|
||||
[alt]="'thumbnail.person.alt'"
|
||||
[placeholder]="'thumbnail.person.placeholder'">
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail
|
||||
[thumbnail]="thumbnail$ | async"
|
||||
[thumbnail]="object?.thumbnail | async"
|
||||
[defaultImage]="'assets/images/project-placeholder.svg'"
|
||||
[alt]="'thumbnail.project.alt'"
|
||||
[placeholder]="'thumbnail.project.placeholder'">
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<div class="d-flex">
|
||||
<!-- <div class="person-thumbnail pr-2">-->
|
||||
<!-- <ds-thumbnail [thumbnail]="getThumbnail() | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>-->
|
||||
<!-- <ds-thumbnail [thumbnail]="dso?.thumbnail | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>-->
|
||||
<!-- </div>-->
|
||||
<div class="flex-grow-1">
|
||||
<ds-org-unit-input-suggestions *ngIf="useNameVariants" [suggestions]="allSuggestions" [(ngModel)]="selectedName" (clickSuggestion)="select($event)"
|
||||
|
@@ -1,8 +1,5 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
|
||||
import { Bitstream } from '../../../../../core/shared/bitstream.model';
|
||||
import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators';
|
||||
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
|
||||
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
@@ -113,11 +110,4 @@ export class OrgUnitSearchResultListSubmissionElementComponent extends SearchRes
|
||||
modalComp.value = value;
|
||||
return modalRef.result;
|
||||
}
|
||||
|
||||
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
|
||||
getThumbnail(): Observable<Bitstream> {
|
||||
return this.bitstreamDataService.getThumbnailFor(this.dso).pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,5 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
|
||||
import { Bitstream } from '../../../../../core/shared/bitstream.model';
|
||||
import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators';
|
||||
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
|
||||
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
@@ -108,11 +105,4 @@ export class PersonSearchResultListSubmissionElementComponent extends SearchResu
|
||||
modalComp.value = value;
|
||||
return modalRef.result;
|
||||
}
|
||||
|
||||
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
|
||||
getThumbnail(): Observable<Bitstream> {
|
||||
return this.bitstreamDataService.getThumbnailFor(this.dso).pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -49,8 +49,8 @@ export class ClaimedTaskSearchResultDetailElementComponent extends SearchResultD
|
||||
*/
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true,
|
||||
followLink('item', null, true, true, true, followLink('bundles')),
|
||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', {},
|
||||
followLink('item', {}, followLink('bundles')),
|
||||
followLink('submitter')
|
||||
), followLink('action'));
|
||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<div class="row mb-1">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||
<ds-thumbnail [thumbnail]="item?.thumbnail | async"></ds-thumbnail>
|
||||
</ds-metadata-field-wrapper>
|
||||
<ng-container *ngVar="(getFiles() | async) as bitstreams">
|
||||
<ds-metadata-field-wrapper [label]="('item.page.files' | translate)">
|
||||
|
@@ -126,13 +126,6 @@ describe('ItemDetailPreviewComponent', () => {
|
||||
|
||||
}));
|
||||
|
||||
it('should get item thumbnail', (done) => {
|
||||
component.getThumbnail().subscribe((thumbnail) => {
|
||||
expect(thumbnail).toBeDefined();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get item bitstreams', (done) => {
|
||||
component.getFiles().subscribe((bitstreams) => {
|
||||
expect(bitstreams).toBeDefined();
|
||||
|
@@ -5,10 +5,7 @@ import { first } from 'rxjs/operators';
|
||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import {
|
||||
getFirstSucceededRemoteDataPayload,
|
||||
getFirstSucceededRemoteListPayload
|
||||
} from '../../../../core/shared/operators';
|
||||
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
|
||||
import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||
import { fadeInOut } from '../../../animations/fade';
|
||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||
@@ -57,11 +54,6 @@ export class ItemDetailPreviewComponent {
|
||||
*/
|
||||
public separator = ', ';
|
||||
|
||||
/**
|
||||
* The item's thumbnail
|
||||
*/
|
||||
public thumbnail$: Observable<Bitstream>;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
@@ -86,13 +78,6 @@ export class ItemDetailPreviewComponent {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO refactor this method to return RemoteData, and the template to deal with loading and errors
|
||||
public getThumbnail(): Observable<Bitstream> {
|
||||
return this.bitstreamDataService.getThumbnailFor(this.item).pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
);
|
||||
}
|
||||
|
||||
// TODO refactor this method to return RemoteData, and the template to deal with loading and errors
|
||||
public getFiles(): Observable<Bitstream[]> {
|
||||
return this.bitstreamDataService
|
||||
|
@@ -48,8 +48,8 @@ export class PoolSearchResultDetailElementComponent extends SearchResultDetailEl
|
||||
*/
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true,
|
||||
followLink('item', null, true, true, true, followLink('bundles')),
|
||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', {},
|
||||
followLink('item', {}, followLink('bundles')),
|
||||
followLink('submitter')
|
||||
), followLink('action'));
|
||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
|
||||
class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</a>
|
||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||
<div>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [limitWidth]="false">
|
||||
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
|
||||
</ds-thumbnail>
|
||||
</div>
|
||||
</span>
|
||||
@@ -43,4 +43,3 @@
|
||||
</ds-truncatable>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
||||
|
@@ -3,13 +3,11 @@ import { Observable } from 'rxjs';
|
||||
|
||||
import { SearchResult } from '../../search/search-result.model';
|
||||
import { BitstreamDataService } from '../../../core/data/bitstream-data.service';
|
||||
import { Bitstream } from '../../../core/shared/bitstream.model';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { Metadata } from '../../../core/shared/metadata.utils';
|
||||
import { hasValue } from '../../empty.util';
|
||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||
import { TruncatableService } from '../../truncatable/truncatable.service';
|
||||
import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-search-result-grid-element',
|
||||
@@ -66,11 +64,4 @@ export class SearchResultGridElementComponent<T extends SearchResult<K>, K exten
|
||||
private isCollapsed(): Observable<boolean> {
|
||||
return this.truncatableService.isCollapsed(this.dso.id);
|
||||
}
|
||||
|
||||
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
|
||||
getThumbnail(): Observable<Bitstream> {
|
||||
return this.bitstreamDataService.getThumbnailFor(this.dso as any).pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -55,10 +55,7 @@ export class ClaimedApprovedSearchResultListElementComponent extends SearchResul
|
||||
super.ngOnInit();
|
||||
this.linkService.resolveLinks(this.dso,
|
||||
followLink('workflowitem',
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
{ useCachedVersionIfAvailable: false },
|
||||
followLink('item'),
|
||||
followLink('submitter')
|
||||
),
|
||||
|
@@ -56,10 +56,7 @@ export class ClaimedDeclinedSearchResultListElementComponent extends SearchResul
|
||||
super.ngOnInit();
|
||||
this.linkService.resolveLinks(this.dso,
|
||||
followLink('workflowitem',
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
{ useCachedVersionIfAvailable: false },
|
||||
followLink('item'),
|
||||
followLink('submitter')
|
||||
),
|
||||
|
@@ -50,7 +50,7 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle
|
||||
*/
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true,
|
||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', {},
|
||||
followLink('item'), followLink('submitter')
|
||||
), followLink('action'));
|
||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||
|
@@ -60,7 +60,7 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
||||
*/
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true,
|
||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', {},
|
||||
followLink('item'), followLink('submitter')
|
||||
), followLink('action'));
|
||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { FindListOptions } from '../../core/data/request.models';
|
||||
import { HALResource } from '../../core/shared/hal-resource.model';
|
||||
import { hasValue } from '../empty.util';
|
||||
|
||||
/**
|
||||
* A class to send the retrieval of a {@link HALLink}
|
||||
@@ -40,6 +41,12 @@ export class FollowLinkConfig<R extends HALResource> {
|
||||
* Defaults to true
|
||||
*/
|
||||
reRequestOnStale? = true;
|
||||
|
||||
/**
|
||||
* If this is false an error will be thrown if the link doesn't exist on the model it is used on
|
||||
* Defaults to false
|
||||
*/
|
||||
isOptional? = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,23 +64,35 @@ export class FollowLinkConfig<R extends HALResource> {
|
||||
* no valid cached version. Defaults
|
||||
* @param reRequestOnStale: Whether or not the link should automatically be re-requested after the
|
||||
* response becomes stale
|
||||
* @param isOptional: Whether or not to fail if the link doesn't exist
|
||||
* @param linksToFollow: a list of {@link FollowLinkConfig}s to
|
||||
* use on the retrieved object.
|
||||
*/
|
||||
export const followLink = <R extends HALResource>(
|
||||
linkName: keyof R['_links'],
|
||||
findListOptions?: FindListOptions,
|
||||
shouldEmbed = true,
|
||||
useCachedVersionIfAvailable = true,
|
||||
reRequestOnStale = true,
|
||||
...linksToFollow: FollowLinkConfig<any>[]
|
||||
): FollowLinkConfig<R> => {
|
||||
return {
|
||||
name: linkName,
|
||||
{
|
||||
findListOptions,
|
||||
shouldEmbed,
|
||||
useCachedVersionIfAvailable,
|
||||
reRequestOnStale,
|
||||
isOptional
|
||||
}: {
|
||||
findListOptions?: FindListOptions,
|
||||
shouldEmbed?: boolean,
|
||||
useCachedVersionIfAvailable?: boolean,
|
||||
reRequestOnStale?: boolean,
|
||||
isOptional?: boolean,
|
||||
} = {},
|
||||
...linksToFollow: FollowLinkConfig<any>[]
|
||||
): FollowLinkConfig<R> => {
|
||||
const followLinkConfig = {
|
||||
name: linkName,
|
||||
findListOptions: hasValue(findListOptions) ? findListOptions : new FindListOptions(),
|
||||
shouldEmbed: hasValue(shouldEmbed) ? shouldEmbed : true,
|
||||
useCachedVersionIfAvailable: hasValue(useCachedVersionIfAvailable) ? useCachedVersionIfAvailable : true,
|
||||
reRequestOnStale: hasValue(reRequestOnStale) ? reRequestOnStale : true,
|
||||
isOptional: hasValue(isOptional) ? isOptional : false,
|
||||
linksToFollow
|
||||
};
|
||||
return followLinkConfig;
|
||||
};
|
||||
|
@@ -78,6 +78,7 @@ describe('ThumbnailComponent', () => {
|
||||
bundle: { href: 'bundle.url' },
|
||||
format: { href: 'format.url' },
|
||||
content: { href: 'content.url' },
|
||||
thumbnail: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -126,6 +127,7 @@ describe('ThumbnailComponent', () => {
|
||||
bundle: { href: 'bundle.url' },
|
||||
format: { href: 'format.url' },
|
||||
content: { href: 'content.url' },
|
||||
thumbnail: undefined,
|
||||
};
|
||||
thumbnail = createSuccessfulRemoteDataObject(bitstream);
|
||||
});
|
||||
|
Reference in New Issue
Block a user