import { Subscription } from 'rxjs';
+import { FaviconService } from '../../../shared/services/favicon.service';
import { SummaryService } from '../../../shared/services/summary.service';
import { TaskManagerService } from '../../../shared/services/task-manager.service';
@Component({
selector: 'cd-workbench-layout',
templateUrl: './workbench-layout.component.html',
- styleUrls: ['./workbench-layout.component.scss']
+ styleUrls: ['./workbench-layout.component.scss'],
+ providers: [FaviconService]
})
export class WorkbenchLayoutComponent implements OnInit, OnDestroy {
private subs = new Subscription();
constructor(
private router: Router,
private summaryService: SummaryService,
- private taskManagerService: TaskManagerService
+ private taskManagerService: TaskManagerService,
+ private faviconService: FaviconService
) {}
ngOnInit() {
this.subs.add(this.summaryService.startPolling());
this.subs.add(this.taskManagerService.init(this.summaryService));
+ this.faviconService.init();
}
ngOnDestroy() {
--- /dev/null
+export enum Color {
+ // HEALTH
+ HEALTH_ERR = '#ff0000',
+ HEALTH_WARN = '#ffa500',
+ HEALTH_OK = '#00bb00'
+}
import { Pipe, PipeTransform } from '@angular/core';
+import { Color } from '../enum/color.enum';
+
@Pipe({
name: 'healthColor'
})
export class HealthColorPipe implements PipeTransform {
transform(value: any): any {
- if (value === 'HEALTH_OK') {
- return { color: '#00bb00' };
- } else if (value === 'HEALTH_WARN') {
- return { color: '#ffa500' };
- } else if (value === 'HEALTH_ERR') {
- return { color: '#ff0000' };
- } else {
- return null;
- }
+ return Color[value] ? { color: Color[value] } : null;
}
}
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+
+import { configureTestBed } from '../../../testing/unit-test-helper';
+import { FaviconService } from './favicon.service';
+
+describe('FaviconService', () => {
+ let service: FaviconService;
+
+ configureTestBed({
+ imports: [HttpClientTestingModule],
+ providers: [FaviconService]
+ });
+
+ beforeEach(() => {
+ service = TestBed.inject(FaviconService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
--- /dev/null
+import { DOCUMENT } from '@angular/common';
+import { Inject, Injectable, OnDestroy } from '@angular/core';
+
+import { Subscription } from 'rxjs';
+
+import { Color } from '../enum/color.enum';
+import { SummaryService } from './summary.service';
+
+@Injectable()
+export class FaviconService implements OnDestroy {
+ sub: Subscription;
+ oldStatus: string;
+ url: string;
+
+ constructor(
+ @Inject(DOCUMENT) private document: HTMLDocument,
+ private summaryService: SummaryService
+ ) {}
+
+ init() {
+ this.url = this.document.getElementById('cdFavicon')?.getAttribute('href');
+
+ this.sub = this.summaryService.subscribe((summary) => {
+ this.changeIcon(summary.health_status);
+ });
+ }
+
+ changeIcon(status?: string) {
+ if (status === this.oldStatus) {
+ return;
+ }
+
+ this.oldStatus = status;
+
+ const favicon = this.document.getElementById('cdFavicon');
+ const faviconSize = 16;
+ const radius = faviconSize / 4;
+
+ const canvas = this.document.createElement('canvas');
+ canvas.width = faviconSize;
+ canvas.height = faviconSize;
+
+ const context = canvas.getContext('2d');
+ const img = this.document.createElement('img');
+ img.src = this.url;
+
+ img.onload = () => {
+ // Draw Original Favicon as Background
+ context.drawImage(img, 0, 0, faviconSize, faviconSize);
+
+ // Cut notification circle area
+ context.save();
+ context.globalCompositeOperation = 'destination-out';
+ context.beginPath();
+ context.arc(canvas.width - radius, radius, radius + 1, 0, 2 * Math.PI);
+ context.fill();
+ context.restore();
+
+ // Draw Notification Circle
+ context.beginPath();
+ context.arc(canvas.width - radius, radius, radius, 0, 2 * Math.PI);
+ context.fillStyle = Color[status] || 'transparent';
+ context.fill();
+
+ // Replace favicon
+ favicon.setAttribute('href', canvas.toDataURL('image/png'));
+ };
+ }
+
+ ngOnDestroy() {
+ this.changeIcon();
+ this.sub?.unsubscribe();
+ }
+}
</script>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
- <link rel="icon" type="image/x-icon" href="favicon.ico">
+ <link rel="icon" type="image/x-icon" id="cdFavicon" href="favicon.ico">
</head>
<body>
<noscript>