@hamak/notification 0.5.1
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/api/index.d.ts +12 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +14 -0
- package/dist/api/interfaces/index.d.ts +5 -0
- package/dist/api/interfaces/index.d.ts.map +1 -0
- package/dist/api/interfaces/index.js +4 -0
- package/dist/api/interfaces/notification-service.d.ts +150 -0
- package/dist/api/interfaces/notification-service.d.ts.map +1 -0
- package/dist/api/interfaces/notification-service.js +1 -0
- package/dist/api/tokens/index.d.ts +5 -0
- package/dist/api/tokens/index.d.ts.map +1 -0
- package/dist/api/tokens/index.js +4 -0
- package/dist/api/tokens/service-tokens.d.ts +22 -0
- package/dist/api/tokens/service-tokens.d.ts.map +1 -0
- package/dist/api/tokens/service-tokens.js +17 -0
- package/dist/api/types/index.d.ts +5 -0
- package/dist/api/types/index.d.ts.map +1 -0
- package/dist/api/types/index.js +4 -0
- package/dist/api/types/notification.d.ts +64 -0
- package/dist/api/types/notification.d.ts.map +1 -0
- package/dist/api/types/notification.js +1 -0
- package/dist/impl/core/index.d.ts +5 -0
- package/dist/impl/core/index.d.ts.map +1 -0
- package/dist/impl/core/index.js +4 -0
- package/dist/impl/core/notification-service.d.ts +75 -0
- package/dist/impl/core/notification-service.d.ts.map +1 -0
- package/dist/impl/core/notification-service.js +269 -0
- package/dist/impl/index.d.ts +16 -0
- package/dist/impl/index.d.ts.map +1 -0
- package/dist/impl/index.js +17 -0
- package/dist/impl/plugin/index.d.ts +5 -0
- package/dist/impl/plugin/index.d.ts.map +1 -0
- package/dist/impl/plugin/index.js +4 -0
- package/dist/impl/plugin/notification-plugin-factory.d.ts +26 -0
- package/dist/impl/plugin/notification-plugin-factory.d.ts.map +1 -0
- package/dist/impl/plugin/notification-plugin-factory.js +120 -0
- package/dist/impl/store/index.d.ts +6 -0
- package/dist/impl/store/index.d.ts.map +1 -0
- package/dist/impl/store/index.js +5 -0
- package/dist/impl/store/notification-actions.d.ts +54 -0
- package/dist/impl/store/notification-actions.d.ts.map +1 -0
- package/dist/impl/store/notification-actions.js +53 -0
- package/dist/impl/store/notification-reducer.d.ts +23 -0
- package/dist/impl/store/notification-reducer.d.ts.map +1 -0
- package/dist/impl/store/notification-reducer.js +47 -0
- package/dist/impl/utils/id-generator.d.ts +14 -0
- package/dist/impl/utils/id-generator.d.ts.map +1 -0
- package/dist/impl/utils/id-generator.js +18 -0
- package/dist/impl/utils/index.d.ts +5 -0
- package/dist/impl/utils/index.d.ts.map +1 -0
- package/dist/impl/utils/index.js +4 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/spi/events/index.d.ts +5 -0
- package/dist/spi/events/index.d.ts.map +1 -0
- package/dist/spi/events/index.js +4 -0
- package/dist/spi/events/notification-events.d.ts +38 -0
- package/dist/spi/events/notification-events.d.ts.map +1 -0
- package/dist/spi/events/notification-events.js +1 -0
- package/dist/spi/index.d.ts +13 -0
- package/dist/spi/index.d.ts.map +1 -0
- package/dist/spi/index.js +13 -0
- package/dist/spi/plugin/index.d.ts +6 -0
- package/dist/spi/plugin/index.d.ts.map +1 -0
- package/dist/spi/plugin/index.js +4 -0
- package/dist/spi/plugin/plugin-config.d.ts +45 -0
- package/dist/spi/plugin/plugin-config.d.ts.map +1 -0
- package/dist/spi/plugin/plugin-config.js +4 -0
- package/dist/spi/plugin/plugin-module.d.ts +11 -0
- package/dist/spi/plugin/plugin-module.d.ts.map +1 -0
- package/dist/spi/plugin/plugin-module.js +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { generateNotificationId } from '../utils/id-generator';
|
|
2
|
+
/**
|
|
3
|
+
* Default configuration values
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
maxNotifications: 50,
|
|
7
|
+
defaultDuration: 5000,
|
|
8
|
+
position: 'top-right',
|
|
9
|
+
enableSound: false,
|
|
10
|
+
enablePersistence: false,
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Core implementation of the notification service
|
|
14
|
+
*/
|
|
15
|
+
export class NotificationService {
|
|
16
|
+
constructor(config, logger) {
|
|
17
|
+
Object.defineProperty(this, "notifications", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: []
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "listeners", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: new Set()
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "config", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: void 0
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(this, "logger", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: void 0
|
|
40
|
+
});
|
|
41
|
+
Object.defineProperty(this, "timers", {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true,
|
|
45
|
+
value: new Map()
|
|
46
|
+
});
|
|
47
|
+
this.config = {
|
|
48
|
+
...DEFAULT_CONFIG,
|
|
49
|
+
persistenceKey: 'hamak:notifications',
|
|
50
|
+
...config,
|
|
51
|
+
};
|
|
52
|
+
this.logger = logger;
|
|
53
|
+
// Load persisted notifications if enabled
|
|
54
|
+
if (this.config.enablePersistence) {
|
|
55
|
+
this.loadFromStorage();
|
|
56
|
+
}
|
|
57
|
+
this.logger.debug('NotificationService initialized', {
|
|
58
|
+
config: this.config,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Create a notification with full control
|
|
63
|
+
*/
|
|
64
|
+
notify(notification) {
|
|
65
|
+
const id = generateNotificationId();
|
|
66
|
+
const timestamp = Date.now();
|
|
67
|
+
const fullNotification = {
|
|
68
|
+
id,
|
|
69
|
+
timestamp,
|
|
70
|
+
duration: notification.duration ?? this.config.defaultDuration,
|
|
71
|
+
...notification,
|
|
72
|
+
};
|
|
73
|
+
// Add to notifications list
|
|
74
|
+
this.notifications.unshift(fullNotification);
|
|
75
|
+
// Enforce max notifications limit
|
|
76
|
+
if (this.notifications.length > this.config.maxNotifications) {
|
|
77
|
+
const removed = this.notifications.splice(this.config.maxNotifications);
|
|
78
|
+
removed.forEach((n) => this.clearTimer(n.id));
|
|
79
|
+
}
|
|
80
|
+
// Setup auto-dismiss timer if duration is set
|
|
81
|
+
if (fullNotification.duration && fullNotification.duration > 0) {
|
|
82
|
+
this.setupAutoDismiss(id, fullNotification.duration);
|
|
83
|
+
}
|
|
84
|
+
// Persist if enabled
|
|
85
|
+
if (this.config.enablePersistence) {
|
|
86
|
+
this.saveToStorage();
|
|
87
|
+
}
|
|
88
|
+
// Notify listeners
|
|
89
|
+
this.notifyListeners();
|
|
90
|
+
this.logger.debug('Notification created', {
|
|
91
|
+
id,
|
|
92
|
+
type: fullNotification.type,
|
|
93
|
+
title: fullNotification.title,
|
|
94
|
+
source: fullNotification.source,
|
|
95
|
+
});
|
|
96
|
+
return id;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Create an info notification
|
|
100
|
+
*/
|
|
101
|
+
info(message, title = 'Info', options) {
|
|
102
|
+
return this.notify({
|
|
103
|
+
type: 'info',
|
|
104
|
+
title,
|
|
105
|
+
message,
|
|
106
|
+
...options,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create a success notification
|
|
111
|
+
*/
|
|
112
|
+
success(message, title = 'Success', options) {
|
|
113
|
+
return this.notify({
|
|
114
|
+
type: 'success',
|
|
115
|
+
title,
|
|
116
|
+
message,
|
|
117
|
+
...options,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Create a warning notification
|
|
122
|
+
*/
|
|
123
|
+
warning(message, title = 'Warning', options) {
|
|
124
|
+
return this.notify({
|
|
125
|
+
type: 'warning',
|
|
126
|
+
title,
|
|
127
|
+
message,
|
|
128
|
+
...options,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Create an error notification
|
|
133
|
+
*/
|
|
134
|
+
error(message, title = 'Error', options) {
|
|
135
|
+
return this.notify({
|
|
136
|
+
type: 'error',
|
|
137
|
+
title,
|
|
138
|
+
message,
|
|
139
|
+
duration: 0, // Errors don't auto-dismiss by default
|
|
140
|
+
...options,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Dismiss a specific notification
|
|
145
|
+
*/
|
|
146
|
+
dismiss(id) {
|
|
147
|
+
const index = this.notifications.findIndex((n) => n.id === id);
|
|
148
|
+
if (index !== -1) {
|
|
149
|
+
this.notifications.splice(index, 1);
|
|
150
|
+
this.clearTimer(id);
|
|
151
|
+
if (this.config.enablePersistence) {
|
|
152
|
+
this.saveToStorage();
|
|
153
|
+
}
|
|
154
|
+
this.notifyListeners();
|
|
155
|
+
this.logger.debug('Notification dismissed', { id });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Dismiss all notifications
|
|
160
|
+
*/
|
|
161
|
+
dismissAll() {
|
|
162
|
+
const count = this.notifications.length;
|
|
163
|
+
this.notifications = [];
|
|
164
|
+
// Clear all timers
|
|
165
|
+
this.timers.forEach((timer) => clearTimeout(timer));
|
|
166
|
+
this.timers.clear();
|
|
167
|
+
if (this.config.enablePersistence) {
|
|
168
|
+
this.saveToStorage();
|
|
169
|
+
}
|
|
170
|
+
this.notifyListeners();
|
|
171
|
+
this.logger.debug('All notifications dismissed', { count });
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get all current notifications
|
|
175
|
+
*/
|
|
176
|
+
getAll() {
|
|
177
|
+
return [...this.notifications];
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Subscribe to notification changes
|
|
181
|
+
*/
|
|
182
|
+
subscribe(listener) {
|
|
183
|
+
this.listeners.add(listener);
|
|
184
|
+
this.logger.debug('Listener subscribed', { listenerCount: this.listeners.size });
|
|
185
|
+
// Return unsubscribe function
|
|
186
|
+
return () => {
|
|
187
|
+
this.listeners.delete(listener);
|
|
188
|
+
this.logger.debug('Listener unsubscribed', { listenerCount: this.listeners.size });
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Cleanup resources
|
|
193
|
+
*/
|
|
194
|
+
destroy() {
|
|
195
|
+
this.timers.forEach((timer) => clearTimeout(timer));
|
|
196
|
+
this.timers.clear();
|
|
197
|
+
this.listeners.clear();
|
|
198
|
+
this.notifications = [];
|
|
199
|
+
this.logger.debug('NotificationService destroyed');
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Setup auto-dismiss timer for a notification
|
|
203
|
+
*/
|
|
204
|
+
setupAutoDismiss(id, duration) {
|
|
205
|
+
const timer = setTimeout(() => {
|
|
206
|
+
this.dismiss(id);
|
|
207
|
+
}, duration);
|
|
208
|
+
this.timers.set(id, timer);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Clear timer for a notification
|
|
212
|
+
*/
|
|
213
|
+
clearTimer(id) {
|
|
214
|
+
const timer = this.timers.get(id);
|
|
215
|
+
if (timer) {
|
|
216
|
+
clearTimeout(timer);
|
|
217
|
+
this.timers.delete(id);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Notify all listeners of changes
|
|
222
|
+
*/
|
|
223
|
+
notifyListeners() {
|
|
224
|
+
const notifications = this.getAll();
|
|
225
|
+
this.listeners.forEach((listener) => {
|
|
226
|
+
try {
|
|
227
|
+
listener(notifications);
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
this.logger.error('Error in notification listener', error instanceof Error ? error : new Error(String(error)));
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Save notifications to localStorage
|
|
236
|
+
*/
|
|
237
|
+
saveToStorage() {
|
|
238
|
+
if (typeof localStorage === 'undefined')
|
|
239
|
+
return;
|
|
240
|
+
try {
|
|
241
|
+
localStorage.setItem(this.config.persistenceKey, JSON.stringify(this.notifications));
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
this.logger.warn('Failed to persist notifications', { message: error instanceof Error ? error.message : String(error) });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Load notifications from localStorage
|
|
249
|
+
*/
|
|
250
|
+
loadFromStorage() {
|
|
251
|
+
if (typeof localStorage === 'undefined')
|
|
252
|
+
return;
|
|
253
|
+
try {
|
|
254
|
+
const stored = localStorage.getItem(this.config.persistenceKey);
|
|
255
|
+
if (stored) {
|
|
256
|
+
const notifications = JSON.parse(stored);
|
|
257
|
+
// Only restore recent notifications (within last hour)
|
|
258
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
259
|
+
this.notifications = notifications.filter((n) => n.timestamp > oneHourAgo);
|
|
260
|
+
this.logger.debug('Notifications loaded from storage', {
|
|
261
|
+
count: this.notifications.length,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
this.logger.warn('Failed to load persisted notifications', { message: error instanceof Error ? error.message : String(error) });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hamak/notification (impl)
|
|
3
|
+
*
|
|
4
|
+
* Core implementation of the notification system.
|
|
5
|
+
* Provides NotificationService, plugin factory, and Redux integration.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export * from './core/index';
|
|
10
|
+
export * from './plugin/index';
|
|
11
|
+
export * from './store/index';
|
|
12
|
+
export * from './utils/index';
|
|
13
|
+
export type { INotification, INotificationService, NotificationType, NotificationOptions, NotificationListener, } from '../api/index';
|
|
14
|
+
export { NOTIFICATION_SERVICE_TOKEN } from '../api/index';
|
|
15
|
+
export type { NotificationPluginConfig, NotificationPosition, NotificationPluginModule, NotificationEvents, } from '../spi/index';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/impl/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,cAAc,cAAc,CAAC;AAG7B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,eAAe,CAAC;AAG9B,cAAc,eAAe,CAAC;AAG9B,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAG1D,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hamak/notification (impl)
|
|
3
|
+
*
|
|
4
|
+
* Core implementation of the notification system.
|
|
5
|
+
* Provides NotificationService, plugin factory, and Redux integration.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
// Export core implementation
|
|
10
|
+
export * from './core/index';
|
|
11
|
+
// Export plugin factory
|
|
12
|
+
export * from './plugin/index';
|
|
13
|
+
// Export store integration
|
|
14
|
+
export * from './store/index';
|
|
15
|
+
// Export utilities
|
|
16
|
+
export * from './utils/index';
|
|
17
|
+
export { NOTIFICATION_SERVICE_TOKEN } from '../api/index';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/impl/plugin/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PluginModule } from '@hamak/microkernel-spi';
|
|
2
|
+
import type { NotificationPluginConfig } from '../../spi/index';
|
|
3
|
+
/**
|
|
4
|
+
* Create a notification plugin for the microkernel
|
|
5
|
+
*
|
|
6
|
+
* @param config - Configuration options for the notification plugin
|
|
7
|
+
* @returns A plugin module ready to be registered with the host
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { createNotificationPlugin } from '@hamak/notification-impl';
|
|
12
|
+
*
|
|
13
|
+
* const notificationPlugin = createNotificationPlugin({
|
|
14
|
+
* maxNotifications: 100,
|
|
15
|
+
* defaultDuration: 3000,
|
|
16
|
+
* position: 'top-right',
|
|
17
|
+
* enablePersistence: true
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* const host = new Host();
|
|
21
|
+
* host.registerPlugin('notification', manifest, notificationPlugin);
|
|
22
|
+
* await host.bootstrapAllAtRoot();
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function createNotificationPlugin(config?: NotificationPluginConfig): PluginModule;
|
|
26
|
+
//# sourceMappingURL=notification-plugin-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-plugin-factory.d.ts","sourceRoot":"","sources":["../../../src/impl/plugin/notification-plugin-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAyB,MAAM,wBAAwB,CAAC;AAIlF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAMhE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,GAAE,wBAA6B,GACpC,YAAY,CAiHd"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { LOG_MANAGER_TOKEN } from '@hamak/logging/api';
|
|
2
|
+
import { NOTIFICATION_SERVICE_TOKEN } from '../../api/index';
|
|
3
|
+
import { NotificationService } from '../core/notification-service';
|
|
4
|
+
import { notificationReducer } from '../store/notification-reducer';
|
|
5
|
+
/**
|
|
6
|
+
* Create a notification plugin for the microkernel
|
|
7
|
+
*
|
|
8
|
+
* @param config - Configuration options for the notification plugin
|
|
9
|
+
* @returns A plugin module ready to be registered with the host
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { createNotificationPlugin } from '@hamak/notification-impl';
|
|
14
|
+
*
|
|
15
|
+
* const notificationPlugin = createNotificationPlugin({
|
|
16
|
+
* maxNotifications: 100,
|
|
17
|
+
* defaultDuration: 3000,
|
|
18
|
+
* position: 'top-right',
|
|
19
|
+
* enablePersistence: true
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* const host = new Host();
|
|
23
|
+
* host.registerPlugin('notification', manifest, notificationPlugin);
|
|
24
|
+
* await host.bootstrapAllAtRoot();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function createNotificationPlugin(config = {}) {
|
|
28
|
+
let notificationService;
|
|
29
|
+
let logger;
|
|
30
|
+
return {
|
|
31
|
+
async initialize(ctx) {
|
|
32
|
+
// Resolve logger
|
|
33
|
+
const logManager = ctx.resolve(LOG_MANAGER_TOKEN);
|
|
34
|
+
logger = logManager.createLogger({ plugin: 'notification', module: 'plugin' });
|
|
35
|
+
// Create notification service
|
|
36
|
+
notificationService = new NotificationService(config, logger);
|
|
37
|
+
// Register service in DI container
|
|
38
|
+
ctx.provide({ provide: NOTIFICATION_SERVICE_TOKEN, useValue: notificationService });
|
|
39
|
+
// Register store reducer if ui-store is available
|
|
40
|
+
try {
|
|
41
|
+
// Try to resolve STORE_EXTENSIONS_TOKEN (optional dependency)
|
|
42
|
+
const STORE_EXTENSIONS_TOKEN = Symbol.for('@hamak/ui-store:StoreExtensionsRegistry');
|
|
43
|
+
const storeExtensions = ctx.resolve(STORE_EXTENSIONS_TOKEN);
|
|
44
|
+
if (storeExtensions && typeof storeExtensions.register === 'function') {
|
|
45
|
+
storeExtensions.register('notification', {
|
|
46
|
+
reducers: {
|
|
47
|
+
notifications: notificationReducer,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
logger.debug('Notification reducer registered with store');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
logger.debug('Store not available, skipping reducer registration', { message: error instanceof Error ? error.message : String(error) });
|
|
55
|
+
}
|
|
56
|
+
// Register commands
|
|
57
|
+
ctx.commands.register('notification.show', (args) => {
|
|
58
|
+
const { type = 'info', message, title, ...options } = args;
|
|
59
|
+
return notificationService.notify({ type, message, title, ...options });
|
|
60
|
+
});
|
|
61
|
+
ctx.commands.register('notification.info', (args) => {
|
|
62
|
+
const { message, title, ...options } = args;
|
|
63
|
+
return notificationService.info(message, title, options);
|
|
64
|
+
});
|
|
65
|
+
ctx.commands.register('notification.success', (args) => {
|
|
66
|
+
const { message, title, ...options } = args;
|
|
67
|
+
return notificationService.success(message, title, options);
|
|
68
|
+
});
|
|
69
|
+
ctx.commands.register('notification.warning', (args) => {
|
|
70
|
+
const { message, title, ...options } = args;
|
|
71
|
+
return notificationService.warning(message, title, options);
|
|
72
|
+
});
|
|
73
|
+
ctx.commands.register('notification.error', (args) => {
|
|
74
|
+
const { message, title, ...options } = args;
|
|
75
|
+
return notificationService.error(message, title, options);
|
|
76
|
+
});
|
|
77
|
+
ctx.commands.register('notification.dismiss', (args) => {
|
|
78
|
+
const { id } = args;
|
|
79
|
+
notificationService.dismiss(id);
|
|
80
|
+
});
|
|
81
|
+
ctx.commands.register('notification.dismissAll', () => {
|
|
82
|
+
notificationService.dismissAll();
|
|
83
|
+
});
|
|
84
|
+
logger.info('Notification plugin initialized', {
|
|
85
|
+
maxNotifications: config.maxNotifications,
|
|
86
|
+
defaultDuration: config.defaultDuration,
|
|
87
|
+
position: config.position,
|
|
88
|
+
enablePersistence: config.enablePersistence,
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
async activate(ctx) {
|
|
92
|
+
// Resolve the notification service
|
|
93
|
+
const service = ctx.resolve(NOTIFICATION_SERVICE_TOKEN);
|
|
94
|
+
// Setup hook listeners for cross-plugin notifications
|
|
95
|
+
ctx.hooks.on('notification:show', (data) => {
|
|
96
|
+
service.notify(data);
|
|
97
|
+
logger.debug('Notification shown via hook', { data });
|
|
98
|
+
});
|
|
99
|
+
// Emit ready event
|
|
100
|
+
ctx.hooks.emit('notification:ready', { service });
|
|
101
|
+
logger.info('Notification plugin activated');
|
|
102
|
+
// Show a welcome notification if in debug mode
|
|
103
|
+
if (config.maxNotifications !== undefined) {
|
|
104
|
+
service.info('Notification system ready', 'System', {
|
|
105
|
+
duration: 3000,
|
|
106
|
+
source: 'notification-plugin',
|
|
107
|
+
metadata: { plugin: 'notification' },
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
async deactivate() {
|
|
112
|
+
logger.info('Notification plugin deactivating');
|
|
113
|
+
// Cleanup
|
|
114
|
+
if (notificationService) {
|
|
115
|
+
notificationService.destroy();
|
|
116
|
+
}
|
|
117
|
+
logger.info('Notification plugin deactivated');
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redux store integration
|
|
3
|
+
*/
|
|
4
|
+
export { notificationReducer, type NotificationState } from './notification-reducer';
|
|
5
|
+
export { NOTIFICATION_ACTIONS, addNotification, dismissNotification, dismissAllNotifications, markNotificationsRead, setAllNotifications, type NotificationAction, } from './notification-actions';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/impl/store/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redux store integration
|
|
3
|
+
*/
|
|
4
|
+
export { notificationReducer } from './notification-reducer';
|
|
5
|
+
export { NOTIFICATION_ACTIONS, addNotification, dismissNotification, dismissAllNotifications, markNotificationsRead, setAllNotifications, } from './notification-actions';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { INotification } from '../../api/index';
|
|
2
|
+
/**
|
|
3
|
+
* Redux action types for notification state management
|
|
4
|
+
*/
|
|
5
|
+
export declare const NOTIFICATION_ACTIONS: {
|
|
6
|
+
readonly ADD: "notification/add";
|
|
7
|
+
readonly DISMISS: "notification/dismiss";
|
|
8
|
+
readonly DISMISS_ALL: "notification/dismissAll";
|
|
9
|
+
readonly MARK_READ: "notification/markRead";
|
|
10
|
+
readonly SET_ALL: "notification/setAll";
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Action creators for notification state
|
|
14
|
+
*/
|
|
15
|
+
export interface NotificationAddAction {
|
|
16
|
+
type: typeof NOTIFICATION_ACTIONS.ADD;
|
|
17
|
+
payload: INotification;
|
|
18
|
+
}
|
|
19
|
+
export interface NotificationDismissAction {
|
|
20
|
+
type: typeof NOTIFICATION_ACTIONS.DISMISS;
|
|
21
|
+
payload: string;
|
|
22
|
+
}
|
|
23
|
+
export interface NotificationDismissAllAction {
|
|
24
|
+
type: typeof NOTIFICATION_ACTIONS.DISMISS_ALL;
|
|
25
|
+
}
|
|
26
|
+
export interface NotificationMarkReadAction {
|
|
27
|
+
type: typeof NOTIFICATION_ACTIONS.MARK_READ;
|
|
28
|
+
}
|
|
29
|
+
export interface NotificationSetAllAction {
|
|
30
|
+
type: typeof NOTIFICATION_ACTIONS.SET_ALL;
|
|
31
|
+
payload: INotification[];
|
|
32
|
+
}
|
|
33
|
+
export type NotificationAction = NotificationAddAction | NotificationDismissAction | NotificationDismissAllAction | NotificationMarkReadAction | NotificationSetAllAction;
|
|
34
|
+
/**
|
|
35
|
+
* Add a notification
|
|
36
|
+
*/
|
|
37
|
+
export declare function addNotification(notification: INotification): NotificationAddAction;
|
|
38
|
+
/**
|
|
39
|
+
* Dismiss a notification by ID
|
|
40
|
+
*/
|
|
41
|
+
export declare function dismissNotification(id: string): NotificationDismissAction;
|
|
42
|
+
/**
|
|
43
|
+
* Dismiss all notifications
|
|
44
|
+
*/
|
|
45
|
+
export declare function dismissAllNotifications(): NotificationDismissAllAction;
|
|
46
|
+
/**
|
|
47
|
+
* Mark all notifications as read
|
|
48
|
+
*/
|
|
49
|
+
export declare function markNotificationsRead(): NotificationMarkReadAction;
|
|
50
|
+
/**
|
|
51
|
+
* Set all notifications (used for syncing with service)
|
|
52
|
+
*/
|
|
53
|
+
export declare function setAllNotifications(notifications: INotification[]): NotificationSetAllAction;
|
|
54
|
+
//# sourceMappingURL=notification-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-actions.d.ts","sourceRoot":"","sources":["../../../src/impl/store/notification-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;CAMvB,CAAC;AAEX;;GAEG;AAEH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,OAAO,oBAAoB,CAAC,GAAG,CAAC;IACtC,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,OAAO,oBAAoB,CAAC,OAAO,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,OAAO,oBAAoB,CAAC,WAAW,CAAC;CAC/C;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,oBAAoB,CAAC,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,oBAAoB,CAAC,OAAO,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,MAAM,kBAAkB,GAC1B,qBAAqB,GACrB,yBAAyB,GACzB,4BAA4B,GAC5B,0BAA0B,GAC1B,wBAAwB,CAAC;AAE7B;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,aAAa,GAAG,qBAAqB,CAKlF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,yBAAyB,CAKzE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,4BAA4B,CAItE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,0BAA0B,CAIlE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,aAAa,EAAE,GAAG,wBAAwB,CAK5F"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redux action types for notification state management
|
|
3
|
+
*/
|
|
4
|
+
export const NOTIFICATION_ACTIONS = {
|
|
5
|
+
ADD: 'notification/add',
|
|
6
|
+
DISMISS: 'notification/dismiss',
|
|
7
|
+
DISMISS_ALL: 'notification/dismissAll',
|
|
8
|
+
MARK_READ: 'notification/markRead',
|
|
9
|
+
SET_ALL: 'notification/setAll',
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Add a notification
|
|
13
|
+
*/
|
|
14
|
+
export function addNotification(notification) {
|
|
15
|
+
return {
|
|
16
|
+
type: NOTIFICATION_ACTIONS.ADD,
|
|
17
|
+
payload: notification,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Dismiss a notification by ID
|
|
22
|
+
*/
|
|
23
|
+
export function dismissNotification(id) {
|
|
24
|
+
return {
|
|
25
|
+
type: NOTIFICATION_ACTIONS.DISMISS,
|
|
26
|
+
payload: id,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Dismiss all notifications
|
|
31
|
+
*/
|
|
32
|
+
export function dismissAllNotifications() {
|
|
33
|
+
return {
|
|
34
|
+
type: NOTIFICATION_ACTIONS.DISMISS_ALL,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Mark all notifications as read
|
|
39
|
+
*/
|
|
40
|
+
export function markNotificationsRead() {
|
|
41
|
+
return {
|
|
42
|
+
type: NOTIFICATION_ACTIONS.MARK_READ,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Set all notifications (used for syncing with service)
|
|
47
|
+
*/
|
|
48
|
+
export function setAllNotifications(notifications) {
|
|
49
|
+
return {
|
|
50
|
+
type: NOTIFICATION_ACTIONS.SET_ALL,
|
|
51
|
+
payload: notifications,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { INotification } from '../../api/index';
|
|
2
|
+
import { type NotificationAction } from './notification-actions';
|
|
3
|
+
/**
|
|
4
|
+
* State shape for notifications in Redux store
|
|
5
|
+
*/
|
|
6
|
+
export interface NotificationState {
|
|
7
|
+
/**
|
|
8
|
+
* All active notifications
|
|
9
|
+
*/
|
|
10
|
+
items: INotification[];
|
|
11
|
+
/**
|
|
12
|
+
* Count of unread notifications
|
|
13
|
+
*/
|
|
14
|
+
unreadCount: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Notification reducer
|
|
18
|
+
*
|
|
19
|
+
* This reducer manages notification state in Redux.
|
|
20
|
+
* It should be kept in sync with the NotificationService via subscriptions.
|
|
21
|
+
*/
|
|
22
|
+
export declare function notificationReducer(state: NotificationState | undefined, action: NotificationAction): NotificationState;
|
|
23
|
+
//# sourceMappingURL=notification-reducer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-reducer.d.ts","sourceRoot":"","sources":["../../../src/impl/store/notification-reducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEvF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,KAAK,EAAE,aAAa,EAAE,CAAC;IAEvB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAUD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,iBAAiB,YAAe,EACvC,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAqCnB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { NOTIFICATION_ACTIONS } from './notification-actions';
|
|
2
|
+
/**
|
|
3
|
+
* Initial state
|
|
4
|
+
*/
|
|
5
|
+
const initialState = {
|
|
6
|
+
items: [],
|
|
7
|
+
unreadCount: 0,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Notification reducer
|
|
11
|
+
*
|
|
12
|
+
* This reducer manages notification state in Redux.
|
|
13
|
+
* It should be kept in sync with the NotificationService via subscriptions.
|
|
14
|
+
*/
|
|
15
|
+
export function notificationReducer(state = initialState, action) {
|
|
16
|
+
switch (action.type) {
|
|
17
|
+
case NOTIFICATION_ACTIONS.ADD:
|
|
18
|
+
return {
|
|
19
|
+
...state,
|
|
20
|
+
items: [action.payload, ...state.items],
|
|
21
|
+
unreadCount: state.unreadCount + 1,
|
|
22
|
+
};
|
|
23
|
+
case NOTIFICATION_ACTIONS.DISMISS:
|
|
24
|
+
return {
|
|
25
|
+
...state,
|
|
26
|
+
items: state.items.filter((n) => n.id !== action.payload),
|
|
27
|
+
};
|
|
28
|
+
case NOTIFICATION_ACTIONS.DISMISS_ALL:
|
|
29
|
+
return {
|
|
30
|
+
...state,
|
|
31
|
+
items: [],
|
|
32
|
+
unreadCount: 0,
|
|
33
|
+
};
|
|
34
|
+
case NOTIFICATION_ACTIONS.MARK_READ:
|
|
35
|
+
return {
|
|
36
|
+
...state,
|
|
37
|
+
unreadCount: 0,
|
|
38
|
+
};
|
|
39
|
+
case NOTIFICATION_ACTIONS.SET_ALL:
|
|
40
|
+
return {
|
|
41
|
+
...state,
|
|
42
|
+
items: action.payload,
|
|
43
|
+
};
|
|
44
|
+
default:
|
|
45
|
+
return state;
|
|
46
|
+
}
|
|
47
|
+
}
|