]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.ts
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / shared / services / notification.service.ts
1 import { Injectable } from '@angular/core';
2
3 import * as _ from 'lodash';
4 import { IndividualConfig, ToastrService } from 'ngx-toastr';
5 import { BehaviorSubject } from 'rxjs';
6
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';
12
13 @Injectable({
14 providedIn: 'root'
15 })
16 export class NotificationService {
17 private hideToasties = false;
18
19 // Observable sources
20 private dataSource = new BehaviorSubject<CdNotification[]>([]);
21
22 // Observable streams
23 data$ = this.dataSource.asObservable();
24
25 private queued: CdNotificationConfig[] = [];
26 private queuedTimeoutId: number;
27 KEY = 'cdNotifications';
28
29 constructor(
30 public toastr: ToastrService,
31 private taskMessageService: TaskMessageService,
32 private cdDatePipe: CdDatePipe
33 ) {
34 const stringNotifications = localStorage.getItem(this.KEY);
35 let notifications: CdNotification[] = [];
36
37 if (_.isString(stringNotifications)) {
38 notifications = JSON.parse(stringNotifications, (_key, value) => {
39 if (_.isPlainObject(value)) {
40 return _.assign(new CdNotification(), value);
41 }
42 return value;
43 });
44 }
45
46 this.dataSource.next(notifications);
47 }
48
49 /**
50 * Removes all current saved notifications
51 */
52 removeAll() {
53 localStorage.removeItem(this.KEY);
54 this.dataSource.next([]);
55 }
56
57 /**
58 * Method used for saving a shown notification (check show() method).
59 */
60 save(notification: CdNotification) {
61 const recent = this.dataSource.getValue();
62 recent.push(notification);
63 while (recent.length > 10) {
64 recent.shift();
65 }
66 this.dataSource.next(recent);
67 localStorage.setItem(this.KEY, JSON.stringify(recent));
68 }
69
70 /**
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.
79 */
80 show(
81 type: NotificationType,
82 title: string,
83 message?: string,
84 options?: any | IndividualConfig,
85 application?: string
86 ): number;
87 show(config: CdNotificationConfig | (() => CdNotificationConfig)): number;
88 show(
89 arg: NotificationType | CdNotificationConfig | (() => CdNotificationConfig),
90 title?: string,
91 message?: string,
92 options?: any | IndividualConfig,
93 application?: string
94 ): number {
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;
101 } else {
102 config = new CdNotificationConfig(
103 arg as NotificationType,
104 title,
105 message,
106 options,
107 application
108 );
109 }
110 this.queueToShow(config);
111 }, 10);
112 }
113
114 private queueToShow(config: CdNotificationConfig) {
115 this.cancel(this.queuedTimeoutId);
116 if (!this.queued.find((c) => _.isEqual(c, config))) {
117 this.queued.push(config);
118 }
119 this.queuedTimeoutId = window.setTimeout(() => {
120 this.showQueued();
121 }, 500);
122 }
123
124 private showQueued() {
125 this.getUnifiedTitleQueue().forEach((config) => {
126 const notification = new CdNotification(config);
127 this.save(notification);
128 this.showToasty(notification);
129 });
130 }
131
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>';
137 }
138 return config;
139 });
140 }
141
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] = [];
148 }
149 byTitle[config.title].push(config);
150 }
151 return byTitle;
152 }
153
154 private showToasty(notification: CdNotification) {
155 // Exit immediately if no toasty should be displayed.
156 if (this.hideToasties) {
157 return;
158 }
159 this.toastr[['error', 'info', 'success'][notification.type]](
160 (notification.message ? notification.message + '<br>' : '') +
161 this.renderTimeAndApplicationHtml(notification),
162 notification.title,
163 notification.options
164 );
165 }
166
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
172 }"></i>`;
173 }
174
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)
181 );
182 } else {
183 notification = new CdNotificationConfig(
184 NotificationType.error,
185 this.taskMessageService.getErrorTitle(finishedTask),
186 this.taskMessageService.getErrorMessage(finishedTask)
187 );
188 }
189 return this.show(notification);
190 }
191
192 /**
193 * Prevent the notification from being shown.
194 * @param {number} timeoutId A number representing the ID of the timeout to be canceled.
195 */
196 cancel(timeoutId) {
197 window.clearTimeout(timeoutId);
198 }
199
200 /**
201 * Suspend showing the notification toasties.
202 * @param {boolean} suspend Set to ``true`` to disable/hide toasties.
203 */
204 suspendToasties(suspend: boolean) {
205 this.hideToasties = suspend;
206 }
207 }