Skip to content

Commit eb9d5ab

Browse files
FrancescoMolinarovins01-4science
authored andcommitted
Merged in task/main-cris/DSC-2309 (pull request DSpace#3137)
Task/main cris/DSC-2309 Approved-by: Vincenzo Mecca
2 parents ca21a72 + 0d127cd commit eb9d5ab

8 files changed

Lines changed: 125 additions & 42 deletions

File tree

src/app/core/shared/image.utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
Observable,
3+
of,
4+
} from 'rxjs';
5+
import { map } from 'rxjs/operators';
6+
7+
export const getDefaultImageUrlByEntityType = (entityType: string): Observable<string> => {
8+
const fallbackImage = 'assets/images/file-placeholder.svg';
9+
10+
if (!entityType) {
11+
return of(fallbackImage);
12+
}
13+
14+
const defaultImage = `assets/images/${entityType.toLowerCase()}-placeholder.svg`;
15+
return checkImageExists(defaultImage).pipe(map((exists) => exists ? defaultImage : fallbackImage));
16+
};
17+
18+
const checkImageExists = (url: string): Observable<boolean> => {
19+
return new Observable<boolean>((observer) => {
20+
const img = new Image();
21+
22+
img.onload = () => {
23+
observer.next(true);
24+
observer.complete();
25+
};
26+
27+
img.onerror = () => {
28+
observer.next(false);
29+
observer.complete();
30+
};
31+
32+
img.src = url;
33+
});
34+
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<ds-thumbnail *ngIf="(initialized | async)" data-test="thumbnail"
22
[thumbnail]="thumbnail$ | async"
3-
[defaultImage]="default">
3+
[defaultImage]="default$ | async">
44
</ds-thumbnail>

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,12 @@ describe('', () => {
169169
expect(thumbnail).toBeTruthy();
170170
}));
171171

172-
it('should show default thumbnail', () => {
173-
expect(component.default).toBe('assets/images/file-placeholder.svg');
172+
it('should show default thumbnail', (done) => {
173+
component.default$.subscribe(image => {
174+
expect(image).toBe('assets/images/file-placeholder.svg');
175+
done();
176+
});
174177
});
175-
176178
});
177179

178180
describe('When bitstreams are only original', () => {
@@ -189,10 +191,12 @@ describe('', () => {
189191
expect(thumbnail).toBeTruthy();
190192
}));
191193

192-
it('should show default thumbnail', () => {
193-
expect(component.default).toBe('assets/images/file-placeholder.svg');
194+
it('should show default thumbnail', (done) => {
195+
component.default$.subscribe(image => {
196+
expect(image).toBe('assets/images/file-placeholder.svg');
197+
done();
198+
});
194199
});
195-
196200
});
197201

198202
describe('When bitstreams are only thumbnail', () => {
@@ -263,10 +267,12 @@ describe('', () => {
263267
expect(thumbnail).toBeTruthy();
264268
});
265269

266-
it('should show default thumbnail', () => {
267-
expect(component.default).toBe('assets/images/file-placeholder.svg');
270+
it('should show default thumbnail', (done) => {
271+
component.default$.subscribe(image => {
272+
expect(image).toBe('assets/images/file-placeholder.svg');
273+
done();
274+
});
268275
});
269-
270276
});
271277

272278
describe('When bitstreams are only original without the right metadata information', () => {
@@ -278,8 +284,11 @@ describe('', () => {
278284
fixture.detectChanges();
279285
});
280286

281-
it('should not show bitstream content image src but the default image', () => {
282-
expect(component.default).toBe('assets/images/file-placeholder.svg');
287+
it('should not show bitstream content image src but the default image', (done) => {
288+
component.default$.subscribe(image => {
289+
expect(image).toBe('assets/images/file-placeholder.svg');
290+
done();
291+
});
283292
});
284293

285294
});
@@ -293,10 +302,12 @@ describe('', () => {
293302
fixture.detectChanges();
294303
});
295304

296-
it('should not show thumbnail content image src but the default image', () => {
297-
expect(component.default).toBe('assets/images/file-placeholder.svg');
305+
it('should not show thumbnail content image src but the default image', (done) => {
306+
component.default$.subscribe(image => {
307+
expect(image).toBe('assets/images/file-placeholder.svg');
308+
done();
309+
});
298310
});
299-
300311
});
301312

302313
describe('When bitstreams are only original with the right metadata information', () => {
@@ -308,8 +319,11 @@ describe('', () => {
308319
fixture.detectChanges();
309320
});
310321

311-
it('should not show thumbnail content image src but the default image', () => {
312-
expect(component.default).toBe('assets/images/file-placeholder.svg');
322+
it('should not show thumbnail content image src but the default image', (done) => {
323+
component.default$.subscribe(image => {
324+
expect(image).toBe('assets/images/file-placeholder.svg');
325+
done();
326+
});
313327
});
314328

315329
});

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.ts

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
import { TranslateService } from '@ngx-translate/core';
1111
import {
1212
BehaviorSubject,
13+
combineLatest,
14+
Observable,
1315
of as observableOf,
1416
} from 'rxjs';
1517
import {
@@ -21,10 +23,10 @@ import { BitstreamDataService } from '../../../../../../../core/data/bitstream-d
2123
import { PaginatedList } from '../../../../../../../core/data/paginated-list.model';
2224
import { LayoutField } from '../../../../../../../core/layout/models/box.model';
2325
import { Bitstream } from '../../../../../../../core/shared/bitstream.model';
26+
import { getDefaultImageUrlByEntityType } from '../../../../../../../core/shared/image.utils';
2427
import { Item } from '../../../../../../../core/shared/item.model';
2528
import { getFirstCompletedRemoteData } from '../../../../../../../core/shared/operators';
2629
import {
27-
hasValue,
2830
isEmpty,
2931
isNotEmpty,
3032
} from '../../../../../../../shared/empty.util';
@@ -56,7 +58,7 @@ export class ThumbnailRenderingComponent extends BitstreamRenderingModelComponen
5658
/**
5759
* Default image to be shown in the thumbnail
5860
*/
59-
default: string;
61+
default$: Observable<string>;
6062

6163
/**
6264
* Item rendering initialization state
@@ -83,9 +85,14 @@ export class ThumbnailRenderingComponent extends BitstreamRenderingModelComponen
8385
* Get the thumbnail information from api for this item
8486
*/
8587
ngOnInit(): void {
86-
this.setDefaultImage();
87-
this.getBitstreamsByItem().pipe(
88-
map((bitstreamList: PaginatedList<Bitstream>) => bitstreamList.page),
88+
const eType = this.item.firstMetadataValue('dspace.entity.type');
89+
this.default$ = getDefaultImageUrlByEntityType(eType);
90+
91+
combineLatest([
92+
this.default$,
93+
this.getBitstreamsByItem(),
94+
]).pipe(
95+
map(([_, bitstreamList]: [string, PaginatedList<Bitstream>]) => bitstreamList.page),
8996
switchMap((filteredBitstreams: Bitstream[]) => {
9097
if (filteredBitstreams.length > 0) {
9198
if (isEmpty(filteredBitstreams[0].thumbnail)) {
@@ -113,19 +120,4 @@ export class ThumbnailRenderingComponent extends BitstreamRenderingModelComponen
113120
this.initialized.next(true);
114121
});
115122
}
116-
117-
/**
118-
* Set the default image src depending on item entity type
119-
*/
120-
setDefaultImage(): void {
121-
const eType = this.item.firstMetadataValue('dspace.entity.type');
122-
this.default = 'assets/images/file-placeholder.svg';
123-
if (hasValue(eType) && eType.toUpperCase() === 'PROJECT') {
124-
this.default = 'assets/images/project-placeholder.svg';
125-
} else if (hasValue(eType) && eType.toUpperCase() === 'ORGUNIT') {
126-
this.default = 'assets/images/orgunit-placeholder.svg';
127-
} else if (hasValue(eType) && eType.toUpperCase() === 'PERSON') {
128-
this.default = 'assets/images/person-placeholder.svg';
129-
}
130-
}
131123
}

src/app/shared/metadata-link-view/metadata-link-view-avatar-popover/metadata-link-view-avatar-popover.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
<img *ngIf="src !== null" class="thumbnail-content img-fluid" [ngClass]="{'d-none': isLoading}"
1111
[src]="src | dsSafeUrl" [alt]="alt | translate" (error)="errorHandler()" (load)="successHandler()">
1212
<div *ngIf="src === null && !isLoading" class="thumbnail-content outer" #thumbnailBox>
13-
<img [src]="fallbackImage" [alt]="placeholder | translate">
13+
<img [src]="placeholderImageUrl$ | async" [alt]="placeholder | translate">
1414
</div>
1515
</div>

src/app/shared/metadata-link-view/metadata-link-view-avatar-popover/metadata-link-view-avatar-popover.component.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,30 @@ describe('MetadataLinkViewAvatarPopoverComponent', () => {
6262
it('should create', () => {
6363
expect(component).toBeTruthy();
6464
});
65+
66+
it('should set fallback image if no entity type', (done) => {
67+
component.ngOnInit();
68+
component.placeholderImageUrl$.subscribe((url) => {
69+
expect(url).toBe('assets/images/file-placeholder.svg');
70+
done();
71+
});
72+
});
73+
74+
it('should set correct placeholder image based on entity type if image exists', (done) => {
75+
component.entityType = 'OrgUnit';
76+
component.ngOnInit();
77+
component.placeholderImageUrl$.subscribe((url) => {
78+
expect(url).toBe('assets/images/orgunit-placeholder.svg');
79+
done();
80+
});
81+
});
82+
83+
it('should set correct fallback image if image does not exists', (done) => {
84+
component.entityType = 'missingEntityType';
85+
component.ngOnInit();
86+
component.placeholderImageUrl$.subscribe((url) => {
87+
expect(url).toBe('assets/images/file-placeholder.svg');
88+
done();
89+
});
90+
});
6591
});

src/app/shared/metadata-link-view/metadata-link-view-avatar-popover/metadata-link-view-avatar-popover.component.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@ import {
33
NgClass,
44
NgIf,
55
} from '@angular/common';
6-
import { Component } from '@angular/core';
6+
import {
7+
Component,
8+
Input,
9+
OnInit,
10+
} from '@angular/core';
711
import { TranslateModule } from '@ngx-translate/core';
12+
import { Observable } from 'rxjs';
813
import { ThumbnailComponent } from 'src/app/thumbnail/thumbnail.component';
914

15+
import { getDefaultImageUrlByEntityType } from '../../../core/shared/image.utils';
1016
import { ThemedLoadingComponent } from '../../loading/themed-loading.component';
1117
import { SafeUrlPipe } from '../../utils/safe-url-pipe';
1218
import { VarDirective } from '../../utils/var.directive';
@@ -26,10 +32,20 @@ import { VarDirective } from '../../utils/var.directive';
2632
],
2733
standalone: true,
2834
})
29-
export class MetadataLinkViewAvatarPopoverComponent extends ThumbnailComponent {
35+
export class MetadataLinkViewAvatarPopoverComponent extends ThumbnailComponent implements OnInit {
36+
37+
38+
/**
39+
* Placeholder image url that changes based on entity type
40+
*/
41+
placeholderImageUrl$: Observable<string>;
3042

3143
/**
32-
* The fallback image to use when the thumbnail is not available
44+
* The entity type of the item which the avatar belong
3345
*/
34-
fallbackImage = 'assets/images/person-placeholder.svg';
46+
@Input() entityType: string;
47+
48+
ngOnInit() {
49+
this.placeholderImageUrl$ = getDefaultImageUrlByEntityType(this.entityType);
50+
}
3551
}

src/app/shared/metadata-link-view/metadata-link-view-popover/metadata-link-view-popover.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<ds-metadata-link-view-avatar-popover
55
*ngIf="item.thumbnail | async"
66
[thumbnail]="item.thumbnail | async"
7+
[entityType]="item.entityType"
78
></ds-metadata-link-view-avatar-popover>
89
<span class="font-weight-bold h4"> {{item.firstMetadataValue('dc.title')}} </span>
910
</div>

0 commit comments

Comments
 (0)