@memberjunction/ng-notifications 5.22.0 → 5.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/module.d.ts +1 -2
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/module.js +3 -8
- package/dist/lib/module.js.map +1 -1
- package/dist/lib/notifications.service.d.ts +21 -3
- package/dist/lib/notifications.service.d.ts.map +1 -1
- package/dist/lib/notifications.service.js +99 -18
- package/dist/lib/notifications.service.js.map +1 -1
- package/package.json +6 -7
package/dist/lib/module.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import * as i1 from "@angular/common";
|
|
3
|
-
import * as i2 from "@progress/kendo-angular-notification";
|
|
4
3
|
export declare class MJNotificationsModule {
|
|
5
4
|
static ɵfac: i0.ɵɵFactoryDeclaration<MJNotificationsModule, never>;
|
|
6
|
-
static ɵmod: i0.ɵɵNgModuleDeclaration<MJNotificationsModule, never, [typeof i1.CommonModule
|
|
5
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<MJNotificationsModule, never, [typeof i1.CommonModule], never>;
|
|
7
6
|
static ɵinj: i0.ɵɵInjectorDeclaration<MJNotificationsModule>;
|
|
8
7
|
}
|
|
9
8
|
//# sourceMappingURL=module.d.ts.map
|
package/dist/lib/module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/lib/module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/lib/module.ts"],"names":[],"mappings":";;AAGA,qBASa,qBAAqB;yCAArB,qBAAqB;0CAArB,qBAAqB;0CAArB,qBAAqB;CAAI"}
|
package/dist/lib/module.js
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
import { NgModule } from '@angular/core';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
|
-
// Kendo UI Angular imports
|
|
4
|
-
import { NotificationModule } from '@progress/kendo-angular-notification';
|
|
5
3
|
import * as i0 from "@angular/core";
|
|
6
4
|
export class MJNotificationsModule {
|
|
7
5
|
static ɵfac = function MJNotificationsModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJNotificationsModule)(); };
|
|
8
6
|
static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: MJNotificationsModule });
|
|
9
|
-
static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [CommonModule
|
|
10
|
-
NotificationModule] });
|
|
7
|
+
static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [CommonModule] });
|
|
11
8
|
}
|
|
12
9
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJNotificationsModule, [{
|
|
13
10
|
type: NgModule,
|
|
14
11
|
args: [{
|
|
15
12
|
declarations: [],
|
|
16
13
|
imports: [
|
|
17
|
-
CommonModule
|
|
18
|
-
NotificationModule
|
|
14
|
+
CommonModule
|
|
19
15
|
],
|
|
20
16
|
exports: []
|
|
21
17
|
}]
|
|
22
18
|
}], null, null); })();
|
|
23
|
-
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MJNotificationsModule, { imports: [CommonModule
|
|
24
|
-
NotificationModule] }); })();
|
|
19
|
+
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MJNotificationsModule, { imports: [CommonModule] }); })();
|
|
25
20
|
//# sourceMappingURL=module.js.map
|
package/dist/lib/module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/lib/module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/lib/module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;;AAW/C,MAAM,OAAO,qBAAqB;+GAArB,qBAAqB;4DAArB,qBAAqB;gEAL9B,YAAY;;iFAKH,qBAAqB;cATjC,QAAQ;eAAC;gBACR,YAAY,EAAE,EACb;gBACD,OAAO,EAAE;oBACP,YAAY;iBACb;gBACD,OAAO,EAAE,EACR;aACF;;wFACY,qBAAqB,cAL9B,YAAY","sourcesContent":["import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@NgModule({\n declarations: [\n ],\n imports: [\n CommonModule\n ],\n exports: [ \n ]\n})\nexport class MJNotificationsModule { }"]}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { MJUserNotificationEntity } from '@memberjunction/core-entities';
|
|
2
|
-
import { NotificationService } from "@progress/kendo-angular-notification";
|
|
3
2
|
import { Observable } from 'rxjs';
|
|
4
3
|
import * as i0 from "@angular/core";
|
|
5
4
|
/**
|
|
@@ -7,7 +6,6 @@ import * as i0 from "@angular/core";
|
|
|
7
6
|
* in the database.
|
|
8
7
|
*/
|
|
9
8
|
export declare class MJNotificationService {
|
|
10
|
-
private notificationService;
|
|
11
9
|
private static readonly _globalStoreKey;
|
|
12
10
|
private static _loaded;
|
|
13
11
|
private static isLoading$;
|
|
@@ -15,7 +13,14 @@ export declare class MJNotificationService {
|
|
|
15
13
|
tabChange$: Observable<unknown>;
|
|
16
14
|
private static _notifications$;
|
|
17
15
|
private static _unreadCount$;
|
|
18
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Optional callback that consuming apps can set to suppress toast notifications
|
|
18
|
+
* for specific events (e.g., when the user is actively viewing the conversation
|
|
19
|
+
* that triggered the notification). Return true to suppress the toast.
|
|
20
|
+
* The notification DB record is still created and the badge count still updates.
|
|
21
|
+
*/
|
|
22
|
+
ShouldSuppressToast?: (statusObj: Record<string, unknown>) => boolean;
|
|
23
|
+
constructor();
|
|
19
24
|
PushStatusUpdates(): Observable<string>;
|
|
20
25
|
static get Instance(): MJNotificationService;
|
|
21
26
|
private static _userNotifications;
|
|
@@ -67,11 +72,24 @@ export declare class MJNotificationService {
|
|
|
67
72
|
static UpdateNotificationObservables(): void;
|
|
68
73
|
/**
|
|
69
74
|
* Creates a message that is not saved to the User Notifications table, but is displayed to the user.
|
|
75
|
+
* Uses a lightweight DOM-based toast notification.
|
|
70
76
|
* @param message - text to display
|
|
71
77
|
* @param style - display styling
|
|
72
78
|
* @param hideAfter - option to auto hide after the specified delay in milliseconds
|
|
73
79
|
*/
|
|
74
80
|
CreateSimpleNotification(message: string, style?: "none" | "success" | "error" | "warning" | "info", hideAfter?: number): void;
|
|
81
|
+
/**
|
|
82
|
+
* Ensures the toast container element exists in the DOM.
|
|
83
|
+
*/
|
|
84
|
+
private ensureToastContainer;
|
|
85
|
+
/**
|
|
86
|
+
* Shows a lightweight toast notification using DOM elements.
|
|
87
|
+
*/
|
|
88
|
+
private showToast;
|
|
89
|
+
/**
|
|
90
|
+
* Escapes HTML entities to prevent XSS in toast messages.
|
|
91
|
+
*/
|
|
92
|
+
private escapeHtml;
|
|
75
93
|
static ɵfac: i0.ɵɵFactoryDeclaration<MJNotificationService, never>;
|
|
76
94
|
static ɵprov: i0.ɵɵInjectableDeclaration<MJNotificationService>;
|
|
77
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.service.d.ts","sourceRoot":"","sources":["../../src/lib/notifications.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzF,OAAO,
|
|
1
|
+
{"version":3,"file":"notifications.service.d.ts","sourceRoot":"","sources":["../../src/lib/notifications.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzF,OAAO,EAAmB,UAAU,EAAW,MAAM,MAAM,CAAC;;AAE5D;;;GAGG;AACH,qBAGa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAyC;IAChF,OAAO,CAAC,MAAM,CAAC,OAAO,CAAkB;IAExC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAuC;IAChE,OAAO,CAAC,SAAS,CAAiB;IAClC,UAAU,sBAAiC;IAG3C,OAAO,CAAC,MAAM,CAAC,eAAe,CAAuD;IACrF,OAAO,CAAC,MAAM,CAAC,aAAa,CAAkC;IAE9D;;;;;OAKG;IACI,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;;IA0FtE,iBAAiB,IAAI,UAAU,CAAC,MAAM,CAAC;IAK9C,WAAkB,QAAQ,IAAI,qBAAqB,CAElD;IAED,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAkC;IACnE,WAAkB,iBAAiB,IAAI,wBAAwB,EAAE,CAEhE;IACD,WAAkB,uBAAuB,IAAI,wBAAwB,EAAE,CAEtE;IACD,WAAkB,2BAA2B,IAAI,MAAM,CAEtD;IAED;;OAEG;IACH,WAAkB,cAAc,IAAI,UAAU,CAAC,wBAAwB,EAAE,CAAC,CAEzE;IAED;;OAEG;IACH,WAAkB,YAAY,IAAI,UAAU,CAAC,MAAM,CAAC,CAEnD;IAED;;OAEG;IACH,IAAW,cAAc,IAAI,UAAU,CAAC,wBAAwB,EAAE,CAAC,CAElE;IAED;;OAEG;IACH,IAAW,YAAY,IAAI,UAAU,CAAC,MAAM,CAAC,CAE5C;IAGD;;;;;;;;OAQG;IACU,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,gBAAgB,EAAE,MAAM,GAAG,IAAI,EAAE,qBAAqB,EAAE,GAAG,GAAG,IAAI,EAAE,aAAa,GAAE,OAAc,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAyBpO;;;;;;;;;OASG;WACiB,wBAAwB;IAc5C;;;;OAIG;WACW,6BAA6B;IAK3C;;;;;;OAMG;IACI,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAkB,EAAE,SAAS,CAAC,EAAE,MAAM;IAIzI;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAe5B;;OAEG;IACH,OAAO,CAAC,SAAS;IA+DjB;;OAEG;IACH,OAAO,CAAC,UAAU;yCAnUP,qBAAqB;6CAArB,qBAAqB;CAwUjC"}
|
|
@@ -4,13 +4,11 @@ import { UserInfoEngine } from '@memberjunction/core-entities';
|
|
|
4
4
|
import { MJEventType, MJGlobal, GetGlobalObjectStore } from '@memberjunction/global';
|
|
5
5
|
import { BehaviorSubject, Subject } from 'rxjs';
|
|
6
6
|
import * as i0 from "@angular/core";
|
|
7
|
-
import * as i1 from "@progress/kendo-angular-notification";
|
|
8
7
|
/**
|
|
9
8
|
* This injectable service is also available as a singleton MJNotificationService.Instance globally within an Angular application/library process space. It is responsible for displaying notifications to the user and also is able to manage the User Notifications entity
|
|
10
9
|
* in the database.
|
|
11
10
|
*/
|
|
12
11
|
export class MJNotificationService {
|
|
13
|
-
notificationService;
|
|
14
12
|
static _globalStoreKey = '___SINGLETON__MJNotificationService';
|
|
15
13
|
static _loaded = false;
|
|
16
14
|
static isLoading$ = new BehaviorSubject(false);
|
|
@@ -19,8 +17,14 @@ export class MJNotificationService {
|
|
|
19
17
|
// Observable for notification changes
|
|
20
18
|
static _notifications$ = new BehaviorSubject([]);
|
|
21
19
|
static _unreadCount$ = new BehaviorSubject(0);
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Optional callback that consuming apps can set to suppress toast notifications
|
|
22
|
+
* for specific events (e.g., when the user is actively viewing the conversation
|
|
23
|
+
* that triggered the notification). Return true to suppress the toast.
|
|
24
|
+
* The notification DB record is still created and the badge count still updates.
|
|
25
|
+
*/
|
|
26
|
+
ShouldSuppressToast;
|
|
27
|
+
constructor() {
|
|
24
28
|
const g = GetGlobalObjectStore();
|
|
25
29
|
if (g[MJNotificationService._globalStoreKey]) {
|
|
26
30
|
return g[MJNotificationService._globalStoreKey];
|
|
@@ -80,7 +84,13 @@ export class MJNotificationService {
|
|
|
80
84
|
const action = statusObj.action?.trim().toLowerCase()
|
|
81
85
|
|| statusObj.details?.action?.trim().toLowerCase();
|
|
82
86
|
if (action === 'create') {
|
|
83
|
-
|
|
87
|
+
// Check if the consuming app wants to suppress this toast
|
|
88
|
+
// (e.g., user is actively viewing the conversation that triggered it)
|
|
89
|
+
const suppress = this.ShouldSuppressToast?.(statusObj) ?? false;
|
|
90
|
+
if (!suppress) {
|
|
91
|
+
this.CreateSimpleNotification(statusObj.title || 'New Notification Available', "success", 3000);
|
|
92
|
+
}
|
|
93
|
+
// Always refresh the notification list (badge count, unread state)
|
|
84
94
|
MJNotificationService.RefreshUserNotifications();
|
|
85
95
|
}
|
|
86
96
|
}
|
|
@@ -200,25 +210,96 @@ export class MJNotificationService {
|
|
|
200
210
|
}
|
|
201
211
|
/**
|
|
202
212
|
* Creates a message that is not saved to the User Notifications table, but is displayed to the user.
|
|
213
|
+
* Uses a lightweight DOM-based toast notification.
|
|
203
214
|
* @param message - text to display
|
|
204
215
|
* @param style - display styling
|
|
205
216
|
* @param hideAfter - option to auto hide after the specified delay in milliseconds
|
|
206
217
|
*/
|
|
207
218
|
CreateSimpleNotification(message, style = "success", hideAfter) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
219
|
+
this.showToast(message, style, hideAfter);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Ensures the toast container element exists in the DOM.
|
|
223
|
+
*/
|
|
224
|
+
ensureToastContainer() {
|
|
225
|
+
let container = document.getElementById('mj-toast-container');
|
|
226
|
+
if (!container) {
|
|
227
|
+
container = document.createElement('div');
|
|
228
|
+
container.id = 'mj-toast-container';
|
|
229
|
+
container.style.cssText = `
|
|
230
|
+
position: fixed; top: 16px; left: 50%; transform: translateX(-50%);
|
|
231
|
+
z-index: 100000; display: flex; flex-direction: column; align-items: center; gap: 8px;
|
|
232
|
+
pointer-events: none;
|
|
233
|
+
`;
|
|
234
|
+
document.body.appendChild(container);
|
|
235
|
+
}
|
|
236
|
+
return container;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Shows a lightweight toast notification using DOM elements.
|
|
240
|
+
*/
|
|
241
|
+
showToast(message, style, hideAfter) {
|
|
242
|
+
const container = this.ensureToastContainer();
|
|
243
|
+
const toast = document.createElement('div');
|
|
244
|
+
toast.style.cssText = `
|
|
245
|
+
pointer-events: auto; padding: 12px 20px; border-radius: 6px; font-size: 14px;
|
|
246
|
+
font-family: inherit; max-width: 500px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
247
|
+
display: flex; align-items: center; gap: 8px; animation: mj-toast-slide-in 0.3s ease-out;
|
|
248
|
+
color: var(--mj-text-inverse);
|
|
249
|
+
`;
|
|
250
|
+
const bgColors = {
|
|
251
|
+
success: 'var(--mj-status-success)',
|
|
252
|
+
error: 'var(--mj-status-error)',
|
|
253
|
+
warning: 'var(--mj-status-warning)',
|
|
254
|
+
info: 'var(--mj-status-info)',
|
|
255
|
+
none: 'var(--mj-text-secondary)'
|
|
214
256
|
};
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
257
|
+
toast.style.backgroundColor = bgColors[style] || bgColors['none'];
|
|
258
|
+
const iconMap = {
|
|
259
|
+
success: '\u2713',
|
|
260
|
+
error: '\u2717',
|
|
261
|
+
warning: '\u26A0',
|
|
262
|
+
info: '\u2139',
|
|
263
|
+
none: ''
|
|
264
|
+
};
|
|
265
|
+
const icon = iconMap[style] || '';
|
|
266
|
+
const removeToast = () => {
|
|
267
|
+
toast.style.animation = 'mj-toast-slide-out 0.3s ease-in forwards';
|
|
268
|
+
toast.addEventListener('animationend', () => toast.remove());
|
|
269
|
+
};
|
|
270
|
+
toast.innerHTML = `
|
|
271
|
+
${icon ? `<span style="font-size:16px;font-weight:bold;">${icon}</span>` : ''}
|
|
272
|
+
<span style="flex:1;">${this.escapeHtml(message)}</span>
|
|
273
|
+
${!hideAfter ? `<button style="background:none;border:none;color:var(--mj-text-inverse);cursor:pointer;font-size:18px;padding:0 0 0 8px;line-height:1;" aria-label="Close">×</button>` : ''}
|
|
274
|
+
`;
|
|
275
|
+
if (!hideAfter) {
|
|
276
|
+
const closeBtn = toast.querySelector('button');
|
|
277
|
+
closeBtn?.addEventListener('click', removeToast);
|
|
278
|
+
}
|
|
279
|
+
container.appendChild(toast);
|
|
280
|
+
// Inject keyframes if not already present
|
|
281
|
+
if (!document.getElementById('mj-toast-keyframes')) {
|
|
282
|
+
const styleEl = document.createElement('style');
|
|
283
|
+
styleEl.id = 'mj-toast-keyframes';
|
|
284
|
+
styleEl.textContent = `
|
|
285
|
+
@keyframes mj-toast-slide-in { from { opacity:0; transform:translateY(-20px); } to { opacity:1; transform:translateY(0); } }
|
|
286
|
+
@keyframes mj-toast-slide-out { from { opacity:1; transform:translateY(0); } to { opacity:0; transform:translateY(-20px); } }
|
|
287
|
+
`;
|
|
288
|
+
document.head.appendChild(styleEl);
|
|
289
|
+
}
|
|
290
|
+
if (hideAfter) {
|
|
291
|
+
setTimeout(removeToast, hideAfter);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Escapes HTML entities to prevent XSS in toast messages.
|
|
296
|
+
*/
|
|
297
|
+
escapeHtml(text) {
|
|
298
|
+
const div = document.createElement('div');
|
|
299
|
+
div.textContent = text;
|
|
300
|
+
return div.innerHTML;
|
|
220
301
|
}
|
|
221
|
-
static ɵfac = function MJNotificationService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJNotificationService)(
|
|
302
|
+
static ɵfac = function MJNotificationService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJNotificationService)(); };
|
|
222
303
|
static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MJNotificationService, factory: MJNotificationService.ɵfac, providedIn: 'root' });
|
|
223
304
|
}
|
|
224
305
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJNotificationService, [{
|
|
@@ -226,5 +307,5 @@ export class MJNotificationService {
|
|
|
226
307
|
args: [{
|
|
227
308
|
providedIn: 'root'
|
|
228
309
|
}]
|
|
229
|
-
}], () => [
|
|
310
|
+
}], () => [], null); })();
|
|
230
311
|
//# sourceMappingURL=notifications.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.service.js","sourceRoot":"","sources":["../../src/lib/notifications.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { ElementRef, Injectable } from '@angular/core';\nimport { LogError, Metadata, StartupManager } from '@memberjunction/core';\nimport { UserInfoEngine, MJUserNotificationEntity } from '@memberjunction/core-entities';\nimport { DisplaySimpleNotificationRequestData, MJEventType, MJGlobal, GetGlobalObjectStore } from '@memberjunction/global';\nimport { GraphQLDataProvider } from '@memberjunction/graphql-dataprovider';\nimport { NotificationService, NotificationSettings } from \"@progress/kendo-angular-notification\";\nimport { BehaviorSubject, Observable, Subject } from 'rxjs';\n\n/**\n * This injectable service is also available as a singleton MJNotificationService.Instance globally within an Angular application/library process space. It is responsible for displaying notifications to the user and also is able to manage the User Notifications entity\n * in the database.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class MJNotificationService {\n private static readonly _globalStoreKey = '___SINGLETON__MJNotificationService';\n private static _loaded: boolean = false;\n\n private static isLoading$ = new BehaviorSubject<boolean>(false);\n private tabChange = new Subject();\n tabChange$ = this.tabChange.asObservable();\n\n // Observable for notification changes\n private static _notifications$ = new BehaviorSubject<MJUserNotificationEntity[]>([]);\n private static _unreadCount$ = new BehaviorSubject<number>(0);\n\n constructor(private notificationService: NotificationService) {\n const g = GetGlobalObjectStore()!;\n if (g[MJNotificationService._globalStoreKey]) {\n return g[MJNotificationService._globalStoreKey] as MJNotificationService;\n }\n g[MJNotificationService._globalStoreKey] = this;\n\n MJGlobal.Instance.GetEventListener(true).subscribe( (event) => {\n switch (event.event) {\n case MJEventType.DisplaySimpleNotificationRequest: \n // received the message to display a notification to the user, so do that...\n const messageData: DisplaySimpleNotificationRequestData = <DisplaySimpleNotificationRequestData>event.args;\n this.CreateSimpleNotification(messageData.message,messageData.style, messageData.DisplayDuration);\n break;\n case MJEventType.ComponentEvent:\n if (event.eventCode === \"UserNotificationsUpdated\") {\n // refresh the user notifications\n MJNotificationService.RefreshUserNotifications();\n }\n break;\n case MJEventType.LoggedIn:\n if (MJNotificationService._loaded === false) {\n // Wait for StartupManager to complete before refreshing notifications.\n // UserInfoEngine (which backs RefreshUserNotifications) is @RegisterForStartup\n // and its _metadataConfigs are only populated during Config() which runs\n // as part of StartupManager.Startup(). Calling RefreshItem() before that\n // completes would silently find no config and return nothing.\n StartupManager.Instance.Startup().then(() => {\n MJNotificationService.RefreshUserNotifications();\n });\n }\n\n // Subscribe to UserInfoEngine's DataChange$ so that when CACHE_INVALIDATION\n // updates the _UserNotifications array, we immediately sync our BehaviorSubjects.\n // This is the primary mechanism for real-time notification updates — it fires\n // whenever BaseEngine processes a remote-invalidate event for User Notifications.\n UserInfoEngine.Instance.DataChange$.subscribe(event => {\n if (event.config.PropertyName === '_UserNotifications') {\n MJNotificationService._userNotifications = UserInfoEngine.Instance.UserNotifications;\n MJNotificationService.UpdateNotificationObservables();\n }\n });\n\n // got the login, now subscribe to push status updates here so we can then raise them as events in MJ Global locally\n this.PushStatusUpdates().subscribe( (message: string) => {\n // Handle undefined/null messages gracefully\n if (!message) {\n return;\n }\n\n const statusObj = JSON.parse(message);\n\n // pass along as an event so anyone else who wants to know about the push status update can do stuff\n MJGlobal.Instance.RaiseEvent({\n event: MJEventType.ComponentEvent,\n eventCode: \"PushStatusUpdates\",\n args: statusObj,\n component: this\n })\n\n const type = statusObj.type?.trim().toLowerCase();\n if (type === 'notification' || type === 'usernotifications') {\n // Server sends type:'notification', legacy used 'usernotifications' — support both\n const action = statusObj.action?.trim().toLowerCase()\n || statusObj.details?.action?.trim().toLowerCase();\n if (action === 'create') {\n this.CreateSimpleNotification(statusObj.title || 'New Notification Available', \"success\", 3000);\n MJNotificationService.RefreshUserNotifications();\n }\n }\n else {\n // otherwise just post it as a simple notification, except Skip messages, we will let Skip handle those\n if (type !== 'askskip' && type !== 'entityobjectstatusmessage' && typeof statusObj.message === 'string') {\n this.CreateSimpleNotification(statusObj.message, \"success\", 2500);\n }\n }\n });\n break;\n } \n });\n }\n\n public PushStatusUpdates(): Observable<string> {\n const gp: GraphQLDataProvider = <GraphQLDataProvider>Metadata.Provider;\n return gp.PushStatusUpdates();\n }\n\n public static get Instance(): MJNotificationService {\n return GetGlobalObjectStore()![MJNotificationService._globalStoreKey] as MJNotificationService;\n }\n \n private static _userNotifications: MJUserNotificationEntity[] = [];\n public static get UserNotifications(): MJUserNotificationEntity[] {\n return MJNotificationService._userNotifications;\n }\n public static get UnreadUserNotifications(): MJUserNotificationEntity[] {\n return MJNotificationService._userNotifications.filter(n => n.Unread);\n }\n public static get UnreadUserNotificationCount(): number {\n return MJNotificationService.UnreadUserNotifications.length;\n }\n\n /**\n * Observable that emits the full list of user notifications whenever they change\n */\n public static get Notifications$(): Observable<MJUserNotificationEntity[]> {\n return MJNotificationService._notifications$.asObservable();\n }\n\n /**\n * Observable that emits the unread notification count whenever it changes\n */\n public static get UnreadCount$(): Observable<number> {\n return MJNotificationService._unreadCount$.asObservable();\n }\n\n /**\n * Instance method to access Notifications$ observable\n */\n public get notifications$(): Observable<MJUserNotificationEntity[]> {\n return MJNotificationService.Notifications$;\n }\n\n /**\n * Instance method to access UnreadCount$ observable\n */\n public get unreadCount$(): Observable<number> {\n return MJNotificationService.UnreadCount$;\n }\n\n\n /**\n * Creates a user notification in the database and refreshes the UI. Returns the notification object.\n * @param title \n * @param message \n * @param resourceTypeId \n * @param resourceRecordId \n * @param resourceConfiguration Any object, it is converted to a string by JSON.stringify and stored in the database\n * @returns \n */\n public async CreateNotification(title: string, message: string, resourceTypeId: string | null, resourceRecordId: string | null, resourceConfiguration: any | null, displayToUser: boolean = true): Promise<MJUserNotificationEntity> {\n const md = new Metadata();\n const notification = <MJUserNotificationEntity>await md.GetEntityObject('MJ: User Notifications');\n notification.Title = title;\n notification.Message = message;\n if (resourceTypeId)\n notification.ResourceTypeID = resourceTypeId;\n if (resourceRecordId)\n notification.ResourceRecordID = resourceRecordId;\n if (resourceConfiguration)\n notification.ResourceConfiguration = JSON.stringify(resourceConfiguration);\n \n notification.UserID = md.CurrentUser.ID;\n notification.Unread = true;\n const result = await notification.Save();\n if (result) {\n MJNotificationService.RefreshUserNotifications();\n }\n\n if (displayToUser)\n this.CreateSimpleNotification(notification.Message, \"success\", 2500);\n\n return notification;\n }\n\n /**\n * Refresh user notifications and re-emit to observables.\n *\n * Uses RefreshItem() to guarantee fresh data from the server. The global\n * CACHE_INVALIDATION listener keeps the engine cache updated for background\n * changes, but when this method is called in response to a PushStatusUpdates\n * message, the cache invalidation event may not have arrived yet (separate\n * WebSocket message, potential race). RefreshItem() eliminates that race by\n * doing a targeted RunView for just notifications.\n */\n public static async RefreshUserNotifications() {\n try {\n const engine = UserInfoEngine.Instance;\n await engine.RefreshItem('_UserNotifications');\n MJNotificationService._userNotifications = engine.UserNotifications;\n MJNotificationService._notifications$.next(engine.UserNotifications);\n MJNotificationService._unreadCount$.next(MJNotificationService.UnreadUserNotificationCount);\n MJNotificationService._loaded = true;\n }\n catch (e) {\n LogError(e);\n }\n }\n\n /**\n * Update notification observables from existing in-memory data without doing a database query.\n * This is efficient for cases where notifications have been modified locally (e.g., marking as read)\n * and we just need to notify subscribers of the change.\n */\n public static UpdateNotificationObservables() {\n MJNotificationService._notifications$.next(MJNotificationService._userNotifications);\n MJNotificationService._unreadCount$.next(MJNotificationService.UnreadUserNotificationCount);\n }\n\n /**\n * Creates a message that is not saved to the User Notifications table, but is displayed to the user.\n * @param message - text to display\n * @param style - display styling\n * @param hideAfter - option to auto hide after the specified delay in milliseconds\n */\n public CreateSimpleNotification(message: string, style: \"none\" | \"success\" | \"error\" | \"warning\" | \"info\" = \"success\", hideAfter?: number) {\n const props: NotificationSettings = {\n content: message,\n cssClass: \"button-notification\",\n animation: { type: \"slide\", duration: 400 },\n position: { horizontal: \"center\", vertical: \"top\" },\n type: { style: style, icon: true }\n }\n if (hideAfter)\n props.hideAfter = hideAfter;\n else\n props.closable = true;\n\n this.notificationService.show(props);\n }\n}"]}
|
|
1
|
+
{"version":3,"file":"notifications.service.js","sourceRoot":"","sources":["../../src/lib/notifications.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { LogError, Metadata, StartupManager } from '@memberjunction/core';\nimport { UserInfoEngine, MJUserNotificationEntity } from '@memberjunction/core-entities';\nimport { DisplaySimpleNotificationRequestData, MJEventType, MJGlobal, GetGlobalObjectStore } from '@memberjunction/global';\nimport { GraphQLDataProvider } from '@memberjunction/graphql-dataprovider';\nimport { BehaviorSubject, Observable, Subject } from 'rxjs';\n\n/**\n * This injectable service is also available as a singleton MJNotificationService.Instance globally within an Angular application/library process space. It is responsible for displaying notifications to the user and also is able to manage the User Notifications entity\n * in the database.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class MJNotificationService {\n private static readonly _globalStoreKey = '___SINGLETON__MJNotificationService';\n private static _loaded: boolean = false;\n\n private static isLoading$ = new BehaviorSubject<boolean>(false);\n private tabChange = new Subject();\n tabChange$ = this.tabChange.asObservable();\n\n // Observable for notification changes\n private static _notifications$ = new BehaviorSubject<MJUserNotificationEntity[]>([]);\n private static _unreadCount$ = new BehaviorSubject<number>(0);\n\n /**\n * Optional callback that consuming apps can set to suppress toast notifications\n * for specific events (e.g., when the user is actively viewing the conversation\n * that triggered the notification). Return true to suppress the toast.\n * The notification DB record is still created and the badge count still updates.\n */\n public ShouldSuppressToast?: (statusObj: Record<string, unknown>) => boolean;\n\n constructor() {\n const g = GetGlobalObjectStore()!;\n if (g[MJNotificationService._globalStoreKey]) {\n return g[MJNotificationService._globalStoreKey] as MJNotificationService;\n }\n g[MJNotificationService._globalStoreKey] = this;\n\n MJGlobal.Instance.GetEventListener(true).subscribe( (event) => {\n switch (event.event) {\n case MJEventType.DisplaySimpleNotificationRequest: \n // received the message to display a notification to the user, so do that...\n const messageData: DisplaySimpleNotificationRequestData = <DisplaySimpleNotificationRequestData>event.args;\n this.CreateSimpleNotification(messageData.message,messageData.style, messageData.DisplayDuration);\n break;\n case MJEventType.ComponentEvent:\n if (event.eventCode === \"UserNotificationsUpdated\") {\n // refresh the user notifications\n MJNotificationService.RefreshUserNotifications();\n }\n break;\n case MJEventType.LoggedIn:\n if (MJNotificationService._loaded === false) {\n // Wait for StartupManager to complete before refreshing notifications.\n // UserInfoEngine (which backs RefreshUserNotifications) is @RegisterForStartup\n // and its _metadataConfigs are only populated during Config() which runs\n // as part of StartupManager.Startup(). Calling RefreshItem() before that\n // completes would silently find no config and return nothing.\n StartupManager.Instance.Startup().then(() => {\n MJNotificationService.RefreshUserNotifications();\n });\n }\n\n // Subscribe to UserInfoEngine's DataChange$ so that when CACHE_INVALIDATION\n // updates the _UserNotifications array, we immediately sync our BehaviorSubjects.\n // This is the primary mechanism for real-time notification updates — it fires\n // whenever BaseEngine processes a remote-invalidate event for User Notifications.\n UserInfoEngine.Instance.DataChange$.subscribe(event => {\n if (event.config.PropertyName === '_UserNotifications') {\n MJNotificationService._userNotifications = UserInfoEngine.Instance.UserNotifications;\n MJNotificationService.UpdateNotificationObservables();\n }\n });\n\n // got the login, now subscribe to push status updates here so we can then raise them as events in MJ Global locally\n this.PushStatusUpdates().subscribe( (message: string) => {\n // Handle undefined/null messages gracefully\n if (!message) {\n return;\n }\n\n const statusObj = JSON.parse(message);\n\n // pass along as an event so anyone else who wants to know about the push status update can do stuff\n MJGlobal.Instance.RaiseEvent({\n event: MJEventType.ComponentEvent,\n eventCode: \"PushStatusUpdates\",\n args: statusObj,\n component: this\n })\n\n const type = statusObj.type?.trim().toLowerCase();\n if (type === 'notification' || type === 'usernotifications') {\n // Server sends type:'notification', legacy used 'usernotifications' — support both\n const action = statusObj.action?.trim().toLowerCase()\n || statusObj.details?.action?.trim().toLowerCase();\n if (action === 'create') {\n // Check if the consuming app wants to suppress this toast\n // (e.g., user is actively viewing the conversation that triggered it)\n const suppress = this.ShouldSuppressToast?.(statusObj) ?? false;\n if (!suppress) {\n this.CreateSimpleNotification(statusObj.title || 'New Notification Available', \"success\", 3000);\n }\n // Always refresh the notification list (badge count, unread state)\n MJNotificationService.RefreshUserNotifications();\n }\n }\n else {\n // otherwise just post it as a simple notification, except Skip messages, we will let Skip handle those\n if (type !== 'askskip' && type !== 'entityobjectstatusmessage' && typeof statusObj.message === 'string') {\n this.CreateSimpleNotification(statusObj.message, \"success\", 2500);\n }\n }\n });\n break;\n } \n });\n }\n\n public PushStatusUpdates(): Observable<string> {\n const gp: GraphQLDataProvider = <GraphQLDataProvider>Metadata.Provider;\n return gp.PushStatusUpdates();\n }\n\n public static get Instance(): MJNotificationService {\n return GetGlobalObjectStore()![MJNotificationService._globalStoreKey] as MJNotificationService;\n }\n \n private static _userNotifications: MJUserNotificationEntity[] = [];\n public static get UserNotifications(): MJUserNotificationEntity[] {\n return MJNotificationService._userNotifications;\n }\n public static get UnreadUserNotifications(): MJUserNotificationEntity[] {\n return MJNotificationService._userNotifications.filter(n => n.Unread);\n }\n public static get UnreadUserNotificationCount(): number {\n return MJNotificationService.UnreadUserNotifications.length;\n }\n\n /**\n * Observable that emits the full list of user notifications whenever they change\n */\n public static get Notifications$(): Observable<MJUserNotificationEntity[]> {\n return MJNotificationService._notifications$.asObservable();\n }\n\n /**\n * Observable that emits the unread notification count whenever it changes\n */\n public static get UnreadCount$(): Observable<number> {\n return MJNotificationService._unreadCount$.asObservable();\n }\n\n /**\n * Instance method to access Notifications$ observable\n */\n public get notifications$(): Observable<MJUserNotificationEntity[]> {\n return MJNotificationService.Notifications$;\n }\n\n /**\n * Instance method to access UnreadCount$ observable\n */\n public get unreadCount$(): Observable<number> {\n return MJNotificationService.UnreadCount$;\n }\n\n\n /**\n * Creates a user notification in the database and refreshes the UI. Returns the notification object.\n * @param title \n * @param message \n * @param resourceTypeId \n * @param resourceRecordId \n * @param resourceConfiguration Any object, it is converted to a string by JSON.stringify and stored in the database\n * @returns \n */\n public async CreateNotification(title: string, message: string, resourceTypeId: string | null, resourceRecordId: string | null, resourceConfiguration: any | null, displayToUser: boolean = true): Promise<MJUserNotificationEntity> {\n const md = new Metadata();\n const notification = <MJUserNotificationEntity>await md.GetEntityObject('MJ: User Notifications');\n notification.Title = title;\n notification.Message = message;\n if (resourceTypeId)\n notification.ResourceTypeID = resourceTypeId;\n if (resourceRecordId)\n notification.ResourceRecordID = resourceRecordId;\n if (resourceConfiguration)\n notification.ResourceConfiguration = JSON.stringify(resourceConfiguration);\n \n notification.UserID = md.CurrentUser.ID;\n notification.Unread = true;\n const result = await notification.Save();\n if (result) {\n MJNotificationService.RefreshUserNotifications();\n }\n\n if (displayToUser)\n this.CreateSimpleNotification(notification.Message, \"success\", 2500);\n\n return notification;\n }\n\n /**\n * Refresh user notifications and re-emit to observables.\n *\n * Uses RefreshItem() to guarantee fresh data from the server. The global\n * CACHE_INVALIDATION listener keeps the engine cache updated for background\n * changes, but when this method is called in response to a PushStatusUpdates\n * message, the cache invalidation event may not have arrived yet (separate\n * WebSocket message, potential race). RefreshItem() eliminates that race by\n * doing a targeted RunView for just notifications.\n */\n public static async RefreshUserNotifications() {\n try {\n const engine = UserInfoEngine.Instance;\n await engine.RefreshItem('_UserNotifications');\n MJNotificationService._userNotifications = engine.UserNotifications;\n MJNotificationService._notifications$.next(engine.UserNotifications);\n MJNotificationService._unreadCount$.next(MJNotificationService.UnreadUserNotificationCount);\n MJNotificationService._loaded = true;\n }\n catch (e) {\n LogError(e);\n }\n }\n\n /**\n * Update notification observables from existing in-memory data without doing a database query.\n * This is efficient for cases where notifications have been modified locally (e.g., marking as read)\n * and we just need to notify subscribers of the change.\n */\n public static UpdateNotificationObservables() {\n MJNotificationService._notifications$.next(MJNotificationService._userNotifications);\n MJNotificationService._unreadCount$.next(MJNotificationService.UnreadUserNotificationCount);\n }\n\n /**\n * Creates a message that is not saved to the User Notifications table, but is displayed to the user.\n * Uses a lightweight DOM-based toast notification.\n * @param message - text to display\n * @param style - display styling\n * @param hideAfter - option to auto hide after the specified delay in milliseconds\n */\n public CreateSimpleNotification(message: string, style: \"none\" | \"success\" | \"error\" | \"warning\" | \"info\" = \"success\", hideAfter?: number) {\n this.showToast(message, style, hideAfter);\n }\n\n /**\n * Ensures the toast container element exists in the DOM.\n */\n private ensureToastContainer(): HTMLElement {\n let container = document.getElementById('mj-toast-container');\n if (!container) {\n container = document.createElement('div');\n container.id = 'mj-toast-container';\n container.style.cssText = `\n position: fixed; top: 16px; left: 50%; transform: translateX(-50%);\n z-index: 100000; display: flex; flex-direction: column; align-items: center; gap: 8px;\n pointer-events: none;\n `;\n document.body.appendChild(container);\n }\n return container;\n }\n\n /**\n * Shows a lightweight toast notification using DOM elements.\n */\n private showToast(message: string, style: string, hideAfter?: number): void {\n const container = this.ensureToastContainer();\n\n const toast = document.createElement('div');\n toast.style.cssText = `\n pointer-events: auto; padding: 12px 20px; border-radius: 6px; font-size: 14px;\n font-family: inherit; max-width: 500px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n display: flex; align-items: center; gap: 8px; animation: mj-toast-slide-in 0.3s ease-out;\n color: var(--mj-text-inverse);\n `;\n\n const bgColors: Record<string, string> = {\n success: 'var(--mj-status-success)',\n error: 'var(--mj-status-error)',\n warning: 'var(--mj-status-warning)',\n info: 'var(--mj-status-info)',\n none: 'var(--mj-text-secondary)'\n };\n toast.style.backgroundColor = bgColors[style] || bgColors['none'];\n\n const iconMap: Record<string, string> = {\n success: '\\u2713',\n error: '\\u2717',\n warning: '\\u26A0',\n info: '\\u2139',\n none: ''\n };\n const icon = iconMap[style] || '';\n\n const removeToast = () => {\n toast.style.animation = 'mj-toast-slide-out 0.3s ease-in forwards';\n toast.addEventListener('animationend', () => toast.remove());\n };\n\n toast.innerHTML = `\n ${icon ? `<span style=\"font-size:16px;font-weight:bold;\">${icon}</span>` : ''}\n <span style=\"flex:1;\">${this.escapeHtml(message)}</span>\n ${!hideAfter ? `<button style=\"background:none;border:none;color:var(--mj-text-inverse);cursor:pointer;font-size:18px;padding:0 0 0 8px;line-height:1;\" aria-label=\"Close\">×</button>` : ''}\n `;\n\n if (!hideAfter) {\n const closeBtn = toast.querySelector('button');\n closeBtn?.addEventListener('click', removeToast);\n }\n\n container.appendChild(toast);\n\n // Inject keyframes if not already present\n if (!document.getElementById('mj-toast-keyframes')) {\n const styleEl = document.createElement('style');\n styleEl.id = 'mj-toast-keyframes';\n styleEl.textContent = `\n @keyframes mj-toast-slide-in { from { opacity:0; transform:translateY(-20px); } to { opacity:1; transform:translateY(0); } }\n @keyframes mj-toast-slide-out { from { opacity:1; transform:translateY(0); } to { opacity:0; transform:translateY(-20px); } }\n `;\n document.head.appendChild(styleEl);\n }\n\n if (hideAfter) {\n setTimeout(removeToast, hideAfter);\n }\n }\n\n /**\n * Escapes HTML entities to prevent XSS in toast messages.\n */\n private escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n}"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-notifications",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.23.0",
|
|
4
4
|
"description": "MemberJunction: Simple Angular library for displaying user notifications",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -22,14 +22,13 @@
|
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"@angular/common": "21.1.3",
|
|
25
|
-
"@angular/core": "21.1.3"
|
|
26
|
-
"@progress/kendo-angular-notification": "22.0.1"
|
|
25
|
+
"@angular/core": "21.1.3"
|
|
27
26
|
},
|
|
28
27
|
"dependencies": {
|
|
29
|
-
"@memberjunction/core": "5.
|
|
30
|
-
"@memberjunction/core-entities": "5.
|
|
31
|
-
"@memberjunction/global": "5.
|
|
32
|
-
"@memberjunction/graphql-dataprovider": "5.
|
|
28
|
+
"@memberjunction/core": "5.23.0",
|
|
29
|
+
"@memberjunction/core-entities": "5.23.0",
|
|
30
|
+
"@memberjunction/global": "5.23.0",
|
|
31
|
+
"@memberjunction/graphql-dataprovider": "5.23.0",
|
|
33
32
|
"tslib": "^2.8.1",
|
|
34
33
|
"rxjs": "^7.8.2"
|
|
35
34
|
},
|