1 import { Injectable } from '@angular/core';
3 import * as _ from 'lodash';
4 import { IndividualConfig, ToastrService } from 'ngx-toastr';
5 import { BehaviorSubject } from 'rxjs';
7 import { NotificationType } from '../enum/notification-type.enum';
8 import { CdNotification, CdNotificationConfig } from '../models/cd-notification';
9 import { FinishedTask } from '../models/finished-task';
10 import { CdDatePipe } from '../pipes/cd-date.pipe';
11 import { TaskMessageService } from './task-message.service';
16 export class NotificationService {
17 private hideToasties = false;
20 private dataSource = new BehaviorSubject<CdNotification[]>([]);
23 data$ = this.dataSource.asObservable();
25 private queued: CdNotificationConfig[] = [];
26 private queuedTimeoutId: number;
27 KEY = 'cdNotifications';
30 public toastr: ToastrService,
31 private taskMessageService: TaskMessageService,
32 private cdDatePipe: CdDatePipe
34 const stringNotifications = localStorage.getItem(this.KEY);
35 let notifications: CdNotification[] = [];
37 if (_.isString(stringNotifications)) {
38 notifications = JSON.parse(stringNotifications, (_key, value) => {
39 if (_.isPlainObject(value)) {
40 return _.assign(new CdNotification(), value);
46 this.dataSource.next(notifications);
50 * Removes all current saved notifications
53 localStorage.removeItem(this.KEY);
54 this.dataSource.next([]);
58 * Method used for saving a shown notification (check show() method).
60 save(notification: CdNotification) {
61 const recent = this.dataSource.getValue();
62 recent.push(notification);
63 while (recent.length > 10) {
66 this.dataSource.next(recent);
67 localStorage.setItem(this.KEY, JSON.stringify(recent));
71 * Method for showing a notification.
72 * @param {NotificationType} type toastr type
73 * @param {string} title
74 * @param {string} [message] The message to be displayed. Note, use this field
75 * for error notifications only.
76 * @param {*} [options] toastr compatible options, used when creating a toastr
77 * @param {string} [application] Only needed if notification comes from an external application
78 * @returns The timeout ID that is set to be able to cancel the notification.
81 type: NotificationType,
84 options?: any | IndividualConfig,
87 show(config: CdNotificationConfig | (() => CdNotificationConfig)): number;
89 arg: NotificationType | CdNotificationConfig | (() => CdNotificationConfig),
92 options?: any | IndividualConfig,
95 return window.setTimeout(() => {
96 let config: CdNotificationConfig;
97 if (_.isFunction(arg)) {
98 config = arg() as CdNotificationConfig;
99 } else if (_.isObject(arg)) {
100 config = arg as CdNotificationConfig;
102 config = new CdNotificationConfig(
103 arg as NotificationType,
110 this.queueToShow(config);
114 private queueToShow(config: CdNotificationConfig) {
115 this.cancel(this.queuedTimeoutId);
116 if (!this.queued.find((c) => _.isEqual(c, config))) {
117 this.queued.push(config);
119 this.queuedTimeoutId = window.setTimeout(() => {
124 private showQueued() {
125 this.getUnifiedTitleQueue().forEach((config) => {
126 const notification = new CdNotification(config);
127 this.save(notification);
128 this.showToasty(notification);
132 private getUnifiedTitleQueue(): CdNotificationConfig[] {
133 return Object.values(this.queueShiftByTitle()).map((configs) => {
134 const config = configs[0];
135 if (configs.length > 1) {
136 config.message = '<ul>' + configs.map((c) => `<li>${c.message}</li>`).join('') + '</ul>';
142 private queueShiftByTitle(): { [key: string]: CdNotificationConfig[] } {
143 const byTitle: { [key: string]: CdNotificationConfig[] } = {};
144 let config: CdNotificationConfig;
145 while ((config = this.queued.shift())) {
146 if (!byTitle[config.title]) {
147 byTitle[config.title] = [];
149 byTitle[config.title].push(config);
154 private showToasty(notification: CdNotification) {
155 // Exit immediately if no toasty should be displayed.
156 if (this.hideToasties) {
159 this.toastr[['error', 'info', 'success'][notification.type]](
160 (notification.message ? notification.message + '<br>' : '') +
161 this.renderTimeAndApplicationHtml(notification),
167 renderTimeAndApplicationHtml(notification: CdNotification): string {
168 return `<small class="date">${this.cdDatePipe.transform(
169 notification.timestamp
170 )}</small><i class="pull-right custom-icon ${notification.applicationClass}" title="${
171 notification.application
175 notifyTask(finishedTask: FinishedTask, success: boolean = true): number {
176 let notification: CdNotificationConfig;
177 if (finishedTask.success && success) {
178 notification = new CdNotificationConfig(
179 NotificationType.success,
180 this.taskMessageService.getSuccessTitle(finishedTask)
183 notification = new CdNotificationConfig(
184 NotificationType.error,
185 this.taskMessageService.getErrorTitle(finishedTask),
186 this.taskMessageService.getErrorMessage(finishedTask)
189 return this.show(notification);
193 * Prevent the notification from being shown.
194 * @param {number} timeoutId A number representing the ID of the timeout to be canceled.
197 window.clearTimeout(timeoutId);
201 * Suspend showing the notification toasties.
202 * @param {boolean} suspend Set to ``true`` to disable/hide toasties.
204 suspendToasties(suspend: boolean) {
205 this.hideToasties = suspend;