new notification system test
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* Notification Manager
|
||||
* Centralized notification system with persistence and tray support
|
||||
*/
|
||||
|
||||
const STORAGE_KEY = 'peardock_notifications';
|
||||
const MAX_NOTIFICATIONS = 100; // Limit stored notifications
|
||||
|
||||
class NotificationManager {
|
||||
constructor() {
|
||||
this.notifications = [];
|
||||
this.listeners = [];
|
||||
this.loadFromStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load notifications from localStorage
|
||||
*/
|
||||
loadFromStorage() {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored);
|
||||
// Convert date strings back to Date objects
|
||||
this.notifications = parsed.map(n => ({
|
||||
...n,
|
||||
timestamp: new Date(n.timestamp)
|
||||
}));
|
||||
// Limit to most recent notifications
|
||||
this.notifications = this.notifications.slice(-MAX_NOTIFICATIONS);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[ERROR] Failed to load notifications from storage:', err);
|
||||
this.notifications = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save notifications to localStorage
|
||||
*/
|
||||
saveToStorage() {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.notifications));
|
||||
} catch (err) {
|
||||
console.error('[ERROR] Failed to save notifications to storage:', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to notification changes
|
||||
* @param {Function} callback - Callback function
|
||||
*/
|
||||
subscribe(callback) {
|
||||
this.listeners.push(callback);
|
||||
// Immediately notify with current state
|
||||
callback(this.notifications, this.getUnreadCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from notification changes
|
||||
* @param {Function} callback - Callback function to remove
|
||||
*/
|
||||
unsubscribe(callback) {
|
||||
this.listeners = this.listeners.filter(listener => listener !== callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all subscribers
|
||||
*/
|
||||
notify() {
|
||||
this.listeners.forEach(callback => {
|
||||
try {
|
||||
callback(this.notifications, this.getUnreadCount());
|
||||
} catch (err) {
|
||||
console.error('[ERROR] Notification listener error:', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new notification
|
||||
* @param {string} type - Notification type (success, danger, warning, info)
|
||||
* @param {string} message - Notification message
|
||||
* @param {Object} options - Additional options (autoDismiss, duration, etc.)
|
||||
* @returns {string} Notification ID
|
||||
*/
|
||||
add(type, message, options = {}) {
|
||||
const id = this.generateId();
|
||||
const notification = {
|
||||
id,
|
||||
type,
|
||||
message,
|
||||
timestamp: new Date(),
|
||||
read: false,
|
||||
autoDismiss: options.autoDismiss !== false, // Default to true
|
||||
duration: options.duration || 5000
|
||||
};
|
||||
|
||||
this.notifications.unshift(notification); // Add to beginning
|
||||
this.notifications = this.notifications.slice(0, MAX_NOTIFICATIONS); // Limit size
|
||||
this.saveToStorage();
|
||||
this.notify();
|
||||
|
||||
// Auto-dismiss if enabled
|
||||
if (notification.autoDismiss) {
|
||||
setTimeout(() => {
|
||||
this.remove(id);
|
||||
}, notification.duration);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a notification
|
||||
* @param {string} id - Notification ID
|
||||
*/
|
||||
remove(id) {
|
||||
this.notifications = this.notifications.filter(n => n.id !== id);
|
||||
this.saveToStorage();
|
||||
this.notify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark notification as read
|
||||
* @param {string} id - Notification ID
|
||||
*/
|
||||
markAsRead(id) {
|
||||
const notification = this.notifications.find(n => n.id === id);
|
||||
if (notification && !notification.read) {
|
||||
notification.read = true;
|
||||
this.saveToStorage();
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all notifications as read
|
||||
*/
|
||||
markAllAsRead() {
|
||||
let changed = false;
|
||||
this.notifications.forEach(n => {
|
||||
if (!n.read) {
|
||||
n.read = true;
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
this.saveToStorage();
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all notifications
|
||||
*/
|
||||
clearAll() {
|
||||
this.notifications = [];
|
||||
this.saveToStorage();
|
||||
this.notify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unread count
|
||||
* @returns {number} Number of unread notifications
|
||||
*/
|
||||
getUnreadCount() {
|
||||
return this.notifications.filter(n => !n.read).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notifications with optional filtering
|
||||
* @param {Object} filters - Filter options (type, read)
|
||||
* @returns {Array} Filtered notifications
|
||||
*/
|
||||
getNotifications(filters = {}) {
|
||||
let filtered = [...this.notifications];
|
||||
|
||||
if (filters.type && filters.type !== 'all') {
|
||||
filtered = filtered.filter(n => n.type === filters.type);
|
||||
}
|
||||
|
||||
if (filters.read !== undefined) {
|
||||
filtered = filtered.filter(n => n.read === filters.read);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique ID for notification
|
||||
* @returns {string} Unique ID
|
||||
*/
|
||||
generateId() {
|
||||
return `notif_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
const notificationManager = new NotificationManager();
|
||||
|
||||
export default notificationManager;
|
||||
|
||||
+7
-19
@@ -3,6 +3,8 @@
|
||||
* Used by both app.js and templateDeploy.js to avoid code duplication
|
||||
*/
|
||||
|
||||
import notificationManager from './notifications.js';
|
||||
|
||||
/**
|
||||
* Close all open Bootstrap modals
|
||||
*/
|
||||
@@ -54,27 +56,13 @@ export function hideStatusIndicator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alert message
|
||||
* Show alert message (now uses notification system)
|
||||
* @param {string} type - Alert type (success, danger, warning, info)
|
||||
* @param {string} message - Message to display
|
||||
* @param {Object} options - Additional options (autoDismiss, duration)
|
||||
*/
|
||||
export function showAlert(type, message) {
|
||||
const alertBox = document.createElement('div');
|
||||
alertBox.className = `alert alert-${type}`;
|
||||
alertBox.textContent = message;
|
||||
|
||||
// Get alert container dynamically to avoid dependency on module-specific variables
|
||||
const container = document.getElementById('alert-container') || document.querySelector('#alert-container');
|
||||
if (container) {
|
||||
container.appendChild(alertBox);
|
||||
|
||||
setTimeout(() => {
|
||||
if (container.contains(alertBox)) {
|
||||
container.removeChild(alertBox);
|
||||
}
|
||||
}, 5000);
|
||||
} else {
|
||||
console.warn('[WARN] Alert container not found.');
|
||||
}
|
||||
export function showAlert(type, message, options = {}) {
|
||||
// Use the new notification system
|
||||
notificationManager.add(type, message, options);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user