Skip to content

Commit 5193d3b

Browse files
[DSC-2309] refactor solution to common util
1 parent f61ab01 commit 5193d3b

5 files changed

Lines changed: 65 additions & 37 deletions

File tree

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

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

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import { Component, Inject, OnInit } from '@angular/core';
22

33
import { BehaviorSubject, of as observableOf } from 'rxjs';
4-
import { map, switchMap } from 'rxjs/operators';
4+
import { map, switchMap, take } from 'rxjs/operators';
55
import { TranslateService } from '@ngx-translate/core';
66

77
import { FieldRenderingType, MetadataBoxFieldRendering } from '../metadata-box.decorator';
88
import { BitstreamDataService } from '../../../../../../../core/data/bitstream-data.service';
9-
import { hasValue, isEmpty, isNotEmpty } from '../../../../../../../shared/empty.util';
9+
import { isEmpty, isNotEmpty } from '../../../../../../../shared/empty.util';
1010
import { Bitstream } from '../../../../../../../core/shared/bitstream.model';
1111
import { BitstreamRenderingModelComponent } from '../bitstream-rendering-model';
1212
import { Item } from '../../../../../../../core/shared/item.model';
1313
import { LayoutField } from '../../../../../../../core/layout/models/box.model';
1414
import { getFirstCompletedRemoteData } from '../../../../../../../core/shared/operators';
1515
import { PaginatedList } from '../../../../../../../core/data/paginated-list.model';
16+
import { getDefaultImageUrlByEntityType } from '../../../../../../../core/shared/image.utils';
1617

1718
@Component({
1819
// eslint-disable-next-line @angular-eslint/component-selector
@@ -97,13 +98,8 @@ export class ThumbnailComponent extends BitstreamRenderingModelComponent impleme
9798
*/
9899
setDefaultImage(): void {
99100
const eType = this.item.firstMetadataValue('dspace.entity.type');
100-
this.default = 'assets/images/file-placeholder.svg';
101-
if (hasValue(eType) && eType.toUpperCase() === 'PROJECT') {
102-
this.default = 'assets/images/project-placeholder.svg';
103-
} else if (hasValue(eType) && eType.toUpperCase() === 'ORGUNIT') {
104-
this.default = 'assets/images/orgunit-placeholder.svg';
105-
} else if (hasValue(eType) && eType.toUpperCase() === 'PERSON') {
106-
this.default = 'assets/images/person-placeholder.svg';
107-
}
101+
getDefaultImageUrlByEntityType(eType).pipe(take(1)).subscribe((url) => {
102+
this.default = url;
103+
});
108104
}
109105
}

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]="placeholderImageUrl" [alt]="placeholder | translate" (error)="onImageError($event)">
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: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,29 @@ describe('MetadataLinkViewAvatarPopoverComponent', () => {
5858
expect(component).toBeTruthy();
5959
});
6060

61-
it('should set placeholder image based on entity type', () => {
62-
component.entityType = 'testEntityType';
61+
it('should set fallback image if no entity type', (done) => {
6362
component.ngOnInit();
64-
expect(component.placeholderImageUrl).toBe('assets/images/testentitytype-placeholder.svg');
63+
component.placeholderImageUrl$.subscribe((url) => {
64+
expect(url).toBe('assets/images/file-placeholder.svg');
65+
done();
66+
});
67+
});
68+
69+
it('should set correct placeholder image based on entity type if image exists', (done) => {
70+
component.entityType = 'OrgUnit';
71+
component.ngOnInit();
72+
component.placeholderImageUrl$.subscribe((url) => {
73+
expect(url).toBe('assets/images/orgunit-placeholder.svg');
74+
done();
75+
});
76+
});
77+
78+
it('should set correct fallback image if image does not exists', (done) => {
79+
component.entityType = 'missingEntityType';
80+
component.ngOnInit();
81+
component.placeholderImageUrl$.subscribe((url) => {
82+
expect(url).toBe('assets/images/file-placeholder.svg');
83+
done();
84+
});
6585
});
6686
});
Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Component, Input, OnInit } from '@angular/core';
22
import { ThumbnailComponent } from 'src/app/thumbnail/thumbnail.component';
3+
import { getDefaultImageUrlByEntityType } from '../../../core/shared/image.utils';
4+
import { Observable } from 'rxjs';
35

46
@Component({
57
selector: 'ds-metadata-link-view-avatar-popover',
@@ -8,39 +10,18 @@ import { ThumbnailComponent } from 'src/app/thumbnail/thumbnail.component';
810
})
911
export class MetadataLinkViewAvatarPopoverComponent extends ThumbnailComponent implements OnInit {
1012

11-
/**
12-
* The fallback image to use when the thumbnail is not available
13-
*/
14-
fallbackImageUrl = 'assets/images/replacement_image.svg';
1513

1614
/**
1715
* Placeholder image url that changes based on entity type
1816
*/
19-
placeholderImageUrl: string;
17+
placeholderImageUrl$: Observable<string>;
2018

2119
/**
2220
* The entity type of the item which the avatar belong
2321
*/
2422
@Input() entityType: string;
2523

2624
ngOnInit() {
27-
this.placeholderImageUrl = this.entityType ? `assets/images/${this.entityType.toLowerCase()}-placeholder.svg` : this.fallbackImageUrl;
28-
}
29-
30-
/**
31-
* Handle error loading image placeholder, e.g. missing placeholder image for specific entity type.
32-
* @param event
33-
*/
34-
onImageError(event: Event) {
35-
const target = event.target as HTMLImageElement;
36-
target.src = this.fallbackImageUrl;
37-
}
38-
39-
/**
40-
* set loading to true to prevent glitch of img with null src
41-
*/
42-
errorHandler() {
43-
this.isLoading = true;
44-
super.errorHandler();
25+
this.placeholderImageUrl$ = getDefaultImageUrlByEntityType(this.entityType);
4526
}
4627
}

0 commit comments

Comments
 (0)