@memberjunction/ng-notifications 5.27.1 → 5.28.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.
|
@@ -11,8 +11,19 @@ export declare class MJNotificationService {
|
|
|
11
11
|
private static isLoading$;
|
|
12
12
|
private tabChange;
|
|
13
13
|
tabChange$: Observable<unknown>;
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Observable stream of the current user's notifications, derived from
|
|
16
|
+
* UserInfoEngine.UserNotifications$. Emits immediately on subscribe (shareReplay
|
|
17
|
+
* buffers the latest value) and re-emits whenever the engine's notifications
|
|
18
|
+
* cache is mutated — by save, delete, remote-invalidate, or refresh. Each
|
|
19
|
+
* emission re-runs the engine's per-user filter+sort.
|
|
20
|
+
*/
|
|
21
|
+
static readonly Notifications$: Observable<MJUserNotificationEntity[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Observable stream of the current user's unread notification count, derived
|
|
24
|
+
* from Notifications$. Same emission lifecycle as Notifications$.
|
|
25
|
+
*/
|
|
26
|
+
static readonly UnreadCount$: Observable<number>;
|
|
16
27
|
/**
|
|
17
28
|
* Optional callback that consuming apps can set to suppress toast notifications
|
|
18
29
|
* for specific events (e.g., when the user is actively viewing the conversation
|
|
@@ -23,18 +34,19 @@ export declare class MJNotificationService {
|
|
|
23
34
|
constructor();
|
|
24
35
|
PushStatusUpdates(): Observable<string>;
|
|
25
36
|
static get Instance(): MJNotificationService;
|
|
26
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The current user's notifications, delegated to UserInfoEngine which owns the
|
|
39
|
+
* cache. Applies the per-user filter and newest-first sort on each access.
|
|
40
|
+
*/
|
|
27
41
|
static get UserNotifications(): MJUserNotificationEntity[];
|
|
28
|
-
static get UnreadUserNotifications(): MJUserNotificationEntity[];
|
|
29
|
-
static get UnreadUserNotificationCount(): number;
|
|
30
42
|
/**
|
|
31
|
-
*
|
|
43
|
+
* The current user's unread notifications, delegated to UserInfoEngine.
|
|
32
44
|
*/
|
|
33
|
-
static get
|
|
45
|
+
static get UnreadUserNotifications(): MJUserNotificationEntity[];
|
|
34
46
|
/**
|
|
35
|
-
*
|
|
47
|
+
* The current user's unread notification count, delegated to UserInfoEngine.
|
|
36
48
|
*/
|
|
37
|
-
static get
|
|
49
|
+
static get UnreadUserNotificationCount(): number;
|
|
38
50
|
/**
|
|
39
51
|
* Instance method to access Notifications$ observable
|
|
40
52
|
*/
|
|
@@ -54,20 +66,22 @@ export declare class MJNotificationService {
|
|
|
54
66
|
*/
|
|
55
67
|
CreateNotification(title: string, message: string, resourceTypeId: string | null, resourceRecordId: string | null, resourceConfiguration: any | null, displayToUser?: boolean): Promise<MJUserNotificationEntity>;
|
|
56
68
|
/**
|
|
57
|
-
* Refresh user notifications
|
|
69
|
+
* Refresh user notifications from the server. Delegates to UserInfoEngine.RefreshItem,
|
|
70
|
+
* whose successful reload mutates the engine's notifications cache and triggers an
|
|
71
|
+
* emission on Notifications$/UnreadCount$ via the BaseEngine observable plumbing —
|
|
72
|
+
* subscribers are updated automatically with no manual re-emit required here.
|
|
58
73
|
*
|
|
59
|
-
* Uses RefreshItem()
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* doing a targeted RunView for just notifications.
|
|
74
|
+
* Uses RefreshItem() rather than relying solely on the global CACHE_INVALIDATION
|
|
75
|
+
* listener: when this method is called in response to a PushStatusUpdates message,
|
|
76
|
+
* the cache-invalidation event may not have arrived yet (separate WebSocket message,
|
|
77
|
+
* potential race). RefreshItem() eliminates that race by doing a targeted RunView
|
|
78
|
+
* for just notifications.
|
|
65
79
|
*/
|
|
66
80
|
static RefreshUserNotifications(): Promise<void>;
|
|
67
81
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
82
|
+
* @deprecated Kept as a no-op for backward compatibility. Observable emissions are now
|
|
83
|
+
* driven by UserInfoEngine.UserNotifications$, which auto-emits on save/delete/
|
|
84
|
+
* remote-invalidate/refresh. External callers no longer need to manually push updates.
|
|
71
85
|
*/
|
|
72
86
|
static UpdateNotificationObservables(): void;
|
|
73
87
|
/**
|
|
@@ -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,EAAmB,UAAU,EAAW,MAAM,MAAM,CAAC;;
|
|
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;;AAG5D;;;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;IAE3C;;;;;;OAMG;IACH,gBAAuB,cAAc,EAAE,UAAU,CAAC,wBAAwB,EAAE,CAAC,CAIzE;IAEJ;;;OAGG;IACH,gBAAuB,YAAY,EAAE,UAAU,CAAC,MAAM,CAAC,CAInD;IAEJ;;;;;OAKG;IACI,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;;IAmFtE,iBAAiB,IAAI,UAAU,CAAC,MAAM,CAAC;IAK9C,WAAkB,QAAQ,IAAI,qBAAqB,CAElD;IAED;;;OAGG;IACH,WAAkB,iBAAiB,IAAI,wBAAwB,EAAE,CAEhE;IAED;;OAEG;IACH,WAAkB,uBAAuB,IAAI,wBAAwB,EAAE,CAEtE;IAED;;OAEG;IACH,WAAkB,2BAA2B,IAAI,MAAM,CAEtD;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;;;;;;;;;;;OAWG;WACiB,wBAAwB;IAU5C;;;;OAIG;WACW,6BAA6B;IAI3C;;;;;;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;yCAzUP,qBAAqB;6CAArB,qBAAqB;CA8UjC"}
|
|
@@ -3,6 +3,7 @@ import { LogError, Metadata, StartupManager } from '@memberjunction/core';
|
|
|
3
3
|
import { UserInfoEngine } from '@memberjunction/core-entities';
|
|
4
4
|
import { MJEventType, MJGlobal, GetGlobalObjectStore } from '@memberjunction/global';
|
|
5
5
|
import { BehaviorSubject, Subject } from 'rxjs';
|
|
6
|
+
import { map, shareReplay } from 'rxjs/operators';
|
|
6
7
|
import * as i0 from "@angular/core";
|
|
7
8
|
/**
|
|
8
9
|
* 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
|
|
@@ -14,9 +15,19 @@ export class MJNotificationService {
|
|
|
14
15
|
static isLoading$ = new BehaviorSubject(false);
|
|
15
16
|
tabChange = new Subject();
|
|
16
17
|
tabChange$ = this.tabChange.asObservable();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Observable stream of the current user's notifications, derived from
|
|
20
|
+
* UserInfoEngine.UserNotifications$. Emits immediately on subscribe (shareReplay
|
|
21
|
+
* buffers the latest value) and re-emits whenever the engine's notifications
|
|
22
|
+
* cache is mutated — by save, delete, remote-invalidate, or refresh. Each
|
|
23
|
+
* emission re-runs the engine's per-user filter+sort.
|
|
24
|
+
*/
|
|
25
|
+
static Notifications$ = UserInfoEngine.Instance.UserNotifications$.pipe(map(() => UserInfoEngine.Instance.UserNotifications), shareReplay({ bufferSize: 1, refCount: false }));
|
|
26
|
+
/**
|
|
27
|
+
* Observable stream of the current user's unread notification count, derived
|
|
28
|
+
* from Notifications$. Same emission lifecycle as Notifications$.
|
|
29
|
+
*/
|
|
30
|
+
static UnreadCount$ = MJNotificationService.Notifications$.pipe(map((arr) => arr.filter((n) => n.Unread).length), shareReplay({ bufferSize: 1, refCount: false }));
|
|
20
31
|
/**
|
|
21
32
|
* Optional callback that consuming apps can set to suppress toast notifications
|
|
22
33
|
* for specific events (e.g., when the user is actively viewing the conversation
|
|
@@ -54,16 +65,9 @@ export class MJNotificationService {
|
|
|
54
65
|
MJNotificationService.RefreshUserNotifications();
|
|
55
66
|
});
|
|
56
67
|
}
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
// whenever BaseEngine processes a remote-invalidate event for User Notifications.
|
|
61
|
-
UserInfoEngine.Instance.DataChange$.subscribe(event => {
|
|
62
|
-
if (event.config.PropertyName === '_UserNotifications') {
|
|
63
|
-
MJNotificationService._userNotifications = UserInfoEngine.Instance.UserNotifications;
|
|
64
|
-
MJNotificationService.UpdateNotificationObservables();
|
|
65
|
-
}
|
|
66
|
-
});
|
|
68
|
+
// Notifications$ / UnreadCount$ are derived from UserInfoEngine.UserNotifications$
|
|
69
|
+
// at static-init time, so they auto-update whenever the engine's cache mutates.
|
|
70
|
+
// No per-login subscription wiring is needed.
|
|
67
71
|
// got the login, now subscribe to push status updates here so we can then raise them as events in MJ Global locally
|
|
68
72
|
this.PushStatusUpdates().subscribe((message) => {
|
|
69
73
|
// Handle undefined/null messages gracefully
|
|
@@ -112,27 +116,24 @@ export class MJNotificationService {
|
|
|
112
116
|
static get Instance() {
|
|
113
117
|
return GetGlobalObjectStore()[MJNotificationService._globalStoreKey];
|
|
114
118
|
}
|
|
115
|
-
|
|
119
|
+
/**
|
|
120
|
+
* The current user's notifications, delegated to UserInfoEngine which owns the
|
|
121
|
+
* cache. Applies the per-user filter and newest-first sort on each access.
|
|
122
|
+
*/
|
|
116
123
|
static get UserNotifications() {
|
|
117
|
-
return
|
|
118
|
-
}
|
|
119
|
-
static get UnreadUserNotifications() {
|
|
120
|
-
return MJNotificationService._userNotifications.filter(n => n.Unread);
|
|
121
|
-
}
|
|
122
|
-
static get UnreadUserNotificationCount() {
|
|
123
|
-
return MJNotificationService.UnreadUserNotifications.length;
|
|
124
|
+
return UserInfoEngine.Instance.UserNotifications;
|
|
124
125
|
}
|
|
125
126
|
/**
|
|
126
|
-
*
|
|
127
|
+
* The current user's unread notifications, delegated to UserInfoEngine.
|
|
127
128
|
*/
|
|
128
|
-
static get
|
|
129
|
-
return
|
|
129
|
+
static get UnreadUserNotifications() {
|
|
130
|
+
return UserInfoEngine.Instance.UnreadNotifications;
|
|
130
131
|
}
|
|
131
132
|
/**
|
|
132
|
-
*
|
|
133
|
+
* The current user's unread notification count, delegated to UserInfoEngine.
|
|
133
134
|
*/
|
|
134
|
-
static get
|
|
135
|
-
return
|
|
135
|
+
static get UnreadUserNotificationCount() {
|
|
136
|
+
return UserInfoEngine.Instance.UnreadNotificationCount;
|
|
136
137
|
}
|
|
137
138
|
/**
|
|
138
139
|
* Instance method to access Notifications$ observable
|
|
@@ -177,22 +178,20 @@ export class MJNotificationService {
|
|
|
177
178
|
return notification;
|
|
178
179
|
}
|
|
179
180
|
/**
|
|
180
|
-
* Refresh user notifications
|
|
181
|
+
* Refresh user notifications from the server. Delegates to UserInfoEngine.RefreshItem,
|
|
182
|
+
* whose successful reload mutates the engine's notifications cache and triggers an
|
|
183
|
+
* emission on Notifications$/UnreadCount$ via the BaseEngine observable plumbing —
|
|
184
|
+
* subscribers are updated automatically with no manual re-emit required here.
|
|
181
185
|
*
|
|
182
|
-
* Uses RefreshItem()
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* doing a targeted RunView for just notifications.
|
|
186
|
+
* Uses RefreshItem() rather than relying solely on the global CACHE_INVALIDATION
|
|
187
|
+
* listener: when this method is called in response to a PushStatusUpdates message,
|
|
188
|
+
* the cache-invalidation event may not have arrived yet (separate WebSocket message,
|
|
189
|
+
* potential race). RefreshItem() eliminates that race by doing a targeted RunView
|
|
190
|
+
* for just notifications.
|
|
188
191
|
*/
|
|
189
192
|
static async RefreshUserNotifications() {
|
|
190
193
|
try {
|
|
191
|
-
|
|
192
|
-
await engine.RefreshItem('_UserNotifications');
|
|
193
|
-
MJNotificationService._userNotifications = engine.UserNotifications;
|
|
194
|
-
MJNotificationService._notifications$.next(engine.UserNotifications);
|
|
195
|
-
MJNotificationService._unreadCount$.next(MJNotificationService.UnreadUserNotificationCount);
|
|
194
|
+
await UserInfoEngine.Instance.RefreshItem('_UserNotifications');
|
|
196
195
|
MJNotificationService._loaded = true;
|
|
197
196
|
}
|
|
198
197
|
catch (e) {
|
|
@@ -200,13 +199,12 @@ export class MJNotificationService {
|
|
|
200
199
|
}
|
|
201
200
|
}
|
|
202
201
|
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
202
|
+
* @deprecated Kept as a no-op for backward compatibility. Observable emissions are now
|
|
203
|
+
* driven by UserInfoEngine.UserNotifications$, which auto-emits on save/delete/
|
|
204
|
+
* remote-invalidate/refresh. External callers no longer need to manually push updates.
|
|
206
205
|
*/
|
|
207
206
|
static UpdateNotificationObservables() {
|
|
208
|
-
|
|
209
|
-
MJNotificationService._unreadCount$.next(MJNotificationService.UnreadUserNotificationCount);
|
|
207
|
+
// No-op — see JSDoc above.
|
|
210
208
|
}
|
|
211
209
|
/**
|
|
212
210
|
* Creates a message that is not saved to the User Notifications table, but is displayed to the user.
|
|
@@ -1 +1 @@
|
|
|
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}"]}
|
|
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';\nimport { map, shareReplay } from 'rxjs/operators';\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 /**\n * Observable stream of the current user's notifications, derived from\n * UserInfoEngine.UserNotifications$. Emits immediately on subscribe (shareReplay\n * buffers the latest value) and re-emits whenever the engine's notifications\n * cache is mutated — by save, delete, remote-invalidate, or refresh. Each\n * emission re-runs the engine's per-user filter+sort.\n */\n public static readonly Notifications$: Observable<MJUserNotificationEntity[]> =\n UserInfoEngine.Instance.UserNotifications$.pipe(\n map(() => UserInfoEngine.Instance.UserNotifications),\n shareReplay({ bufferSize: 1, refCount: false })\n );\n\n /**\n * Observable stream of the current user's unread notification count, derived\n * from Notifications$. Same emission lifecycle as Notifications$.\n */\n public static readonly UnreadCount$: Observable<number> =\n MJNotificationService.Notifications$.pipe(\n map((arr) => arr.filter((n) => n.Unread).length),\n shareReplay({ bufferSize: 1, refCount: false })\n );\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 // Notifications$ / UnreadCount$ are derived from UserInfoEngine.UserNotifications$\n // at static-init time, so they auto-update whenever the engine's cache mutates.\n // No per-login subscription wiring is needed.\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 /**\n * The current user's notifications, delegated to UserInfoEngine which owns the\n * cache. Applies the per-user filter and newest-first sort on each access.\n */\n public static get UserNotifications(): MJUserNotificationEntity[] {\n return UserInfoEngine.Instance.UserNotifications;\n }\n\n /**\n * The current user's unread notifications, delegated to UserInfoEngine.\n */\n public static get UnreadUserNotifications(): MJUserNotificationEntity[] {\n return UserInfoEngine.Instance.UnreadNotifications;\n }\n\n /**\n * The current user's unread notification count, delegated to UserInfoEngine.\n */\n public static get UnreadUserNotificationCount(): number {\n return UserInfoEngine.Instance.UnreadNotificationCount;\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 from the server. Delegates to UserInfoEngine.RefreshItem,\n * whose successful reload mutates the engine's notifications cache and triggers an\n * emission on Notifications$/UnreadCount$ via the BaseEngine observable plumbing —\n * subscribers are updated automatically with no manual re-emit required here.\n *\n * Uses RefreshItem() rather than relying solely on the global CACHE_INVALIDATION\n * listener: when this method is called in response to a PushStatusUpdates message,\n * the cache-invalidation event may not have arrived yet (separate WebSocket message,\n * potential race). RefreshItem() eliminates that race by doing a targeted RunView\n * for just notifications.\n */\n public static async RefreshUserNotifications() {\n try {\n await UserInfoEngine.Instance.RefreshItem('_UserNotifications');\n MJNotificationService._loaded = true;\n }\n catch (e) {\n LogError(e);\n }\n }\n\n /**\n * @deprecated Kept as a no-op for backward compatibility. Observable emissions are now\n * driven by UserInfoEngine.UserNotifications$, which auto-emits on save/delete/\n * remote-invalidate/refresh. External callers no longer need to manually push updates.\n */\n public static UpdateNotificationObservables() {\n // No-op — see JSDoc above.\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.28.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",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"@angular/core": "21.1.3"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@memberjunction/core": "5.
|
|
29
|
-
"@memberjunction/core-entities": "5.
|
|
30
|
-
"@memberjunction/global": "5.
|
|
31
|
-
"@memberjunction/graphql-dataprovider": "5.
|
|
28
|
+
"@memberjunction/core": "5.28.0",
|
|
29
|
+
"@memberjunction/core-entities": "5.28.0",
|
|
30
|
+
"@memberjunction/global": "5.28.0",
|
|
31
|
+
"@memberjunction/graphql-dataprovider": "5.28.0",
|
|
32
32
|
"tslib": "^2.8.1",
|
|
33
33
|
"rxjs": "^7.8.2"
|
|
34
34
|
},
|