/** * FediStream Notifications JavaScript * * @package WP_FediStream */ ( function( $ ) { 'use strict'; const Notifications = { unreadCount: 0, isOpen: false, pollTimer: null, init: function() { this.createDropdown(); this.bindEvents(); this.loadNotifications(); this.startPolling(); }, createDropdown: function() { const dropdown = ` `; $( '.fedistream-notifications-menu' ).append( dropdown ); }, bindEvents: function() { const self = this; // Toggle dropdown. $( document ).on( 'click', '#wp-admin-bar-fedistream-notifications > a', function( e ) { e.preventDefault(); self.toggleDropdown(); } ); // Close dropdown when clicking outside. $( document ).on( 'click', function( e ) { if ( ! $( e.target ).closest( '.fedistream-notifications-menu' ).length ) { self.closeDropdown(); } } ); // Mark all as read. $( document ).on( 'click', '.fedistream-notifications-dropdown .mark-all-read', function( e ) { e.preventDefault(); e.stopPropagation(); self.markAllRead(); } ); // Mark single as read. $( document ).on( 'click', '.notification-item', function() { const id = $( this ).data( 'id' ); const isRead = $( this ).hasClass( 'is-read' ); if ( ! isRead ) { self.markRead( id, $( this ) ); } } ); // Delete notification. $( document ).on( 'click', '.notification-item .delete-btn', function( e ) { e.preventDefault(); e.stopPropagation(); const item = $( this ).closest( '.notification-item' ); const id = item.data( 'id' ); self.deleteNotification( id, item ); } ); }, toggleDropdown: function() { if ( this.isOpen ) { this.closeDropdown(); } else { this.openDropdown(); } }, openDropdown: function() { $( '.fedistream-notifications-dropdown' ).show(); this.isOpen = true; this.loadNotifications(); }, closeDropdown: function() { $( '.fedistream-notifications-dropdown' ).hide(); this.isOpen = false; }, loadNotifications: function() { const self = this; $.ajax( { url: fedistreamNotifications.ajaxUrl, type: 'POST', data: { action: 'fedistream_get_notifications', nonce: fedistreamNotifications.nonce, limit: 10 }, success: function( response ) { if ( response.success ) { self.renderNotifications( response.data.notifications ); self.updateUnreadCount( response.data.unread_count ); } }, error: function() { $( '.notifications-list' ).html( '

' + fedistreamNotifications.i18n.error + '

' ); } } ); }, renderNotifications: function( notifications ) { const container = $( '.notifications-list' ); container.empty(); if ( ! notifications || notifications.length === 0 ) { container.html( '

' + fedistreamNotifications.i18n.noNotifications + '

' ); return; } notifications.forEach( function( notification ) { container.append( this.createNotificationItem( notification ) ); }, this ); }, createNotificationItem: function( notification ) { const isRead = notification.is_read ? 'is-read' : ''; const icon = this.getNotificationIcon( notification.type ); const time = this.formatTime( notification.created_at ); const link = this.getNotificationLink( notification ); return `
${this.escapeHtml( notification.title )}
${this.escapeHtml( notification.message )}
${time}
${link ? `` : ''}
`; }, getNotificationIcon: function( type ) { const icons = { 'new_release': 'album', 'new_follower': 'admin-users', 'fediverse_like': 'heart', 'fediverse_boost': 'megaphone', 'playlist_added': 'playlist-audio', 'purchase': 'cart', 'system': 'info' }; return icons[ type ] || 'bell'; }, getNotificationLink: function( notification ) { const data = notification.data || {}; if ( data.album_url ) { return data.album_url; } if ( data.track_url ) { return data.track_url; } if ( data.artist_url ) { return data.artist_url; } return null; }, markRead: function( notificationId, element ) { const self = this; $.ajax( { url: fedistreamNotifications.ajaxUrl, type: 'POST', data: { action: 'fedistream_mark_notification_read', nonce: fedistreamNotifications.nonce, notification_id: notificationId }, success: function( response ) { if ( response.success ) { element.addClass( 'is-read' ); self.updateUnreadCount( response.data.unread_count ); } } } ); }, markAllRead: function() { const self = this; $.ajax( { url: fedistreamNotifications.ajaxUrl, type: 'POST', data: { action: 'fedistream_mark_all_notifications_read', nonce: fedistreamNotifications.nonce }, success: function( response ) { if ( response.success ) { $( '.notification-item' ).addClass( 'is-read' ); self.updateUnreadCount( 0 ); } } } ); }, deleteNotification: function( notificationId, element ) { const self = this; $.ajax( { url: fedistreamNotifications.ajaxUrl, type: 'POST', data: { action: 'fedistream_delete_notification', nonce: fedistreamNotifications.nonce, notification_id: notificationId }, success: function( response ) { if ( response.success ) { element.fadeOut( 200, function() { $( this ).remove(); // Check if list is empty. if ( $( '.notification-item' ).length === 0 ) { $( '.notifications-list' ).html( '

' + fedistreamNotifications.i18n.noNotifications + '

' ); } } ); self.updateUnreadCount( response.data.unread_count ); } } } ); }, updateUnreadCount: function( count ) { this.unreadCount = count; const badge = $( '.fedistream-notification-count' ); if ( count > 0 ) { if ( badge.length ) { badge.text( count ); } else { $( '#wp-admin-bar-fedistream-notifications .ab-icon' ).after( '' + count + '' ); } } else { badge.remove(); } }, startPolling: function() { const self = this; const interval = fedistreamNotifications.pollInterval || 60000; this.pollTimer = setInterval( function() { if ( ! self.isOpen ) { self.checkForNewNotifications(); } }, interval ); }, checkForNewNotifications: function() { const self = this; $.ajax( { url: fedistreamNotifications.ajaxUrl, type: 'POST', data: { action: 'fedistream_get_notifications', nonce: fedistreamNotifications.nonce, unread_only: true, limit: 1 }, success: function( response ) { if ( response.success ) { self.updateUnreadCount( response.data.unread_count ); } } } ); }, formatTime: function( dateString ) { const date = new Date( dateString ); const now = new Date(); const diff = Math.floor( ( now - date ) / 1000 ); if ( diff < 60 ) { return fedistreamNotifications.i18n.justNow || 'Just now'; } else if ( diff < 3600 ) { const mins = Math.floor( diff / 60 ); return mins + 'm ago'; } else if ( diff < 86400 ) { const hours = Math.floor( diff / 3600 ); return hours + 'h ago'; } else if ( diff < 604800 ) { const days = Math.floor( diff / 86400 ); return days + 'd ago'; } else { return date.toLocaleDateString(); } }, escapeHtml: function( text ) { const div = document.createElement( 'div' ); div.textContent = text; return div.innerHTML; } }; // Initialize on document ready. $( document ).ready( function() { if ( $( '.fedistream-notifications-menu' ).length ) { Notifications.init(); } } ); } )( jQuery );