You've already forked wp-fedistream
354 lines
8.9 KiB
JavaScript
354 lines
8.9 KiB
JavaScript
|
|
/**
|
||
|
|
* 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 = `
|
||
|
|
<div class="fedistream-notifications-dropdown" style="display: none;">
|
||
|
|
<div class="notifications-header">
|
||
|
|
<h4>${fedistreamNotifications.i18n.viewAll || 'Notifications'}</h4>
|
||
|
|
<button class="mark-all-read" title="${fedistreamNotifications.i18n.markAllRead}">
|
||
|
|
<span class="dashicons dashicons-yes-alt"></span>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div class="notifications-list">
|
||
|
|
<div class="loading">${fedistreamNotifications.i18n.loading || 'Loading...'}</div>
|
||
|
|
</div>
|
||
|
|
<div class="notifications-footer">
|
||
|
|
<a href="#" class="view-all">${fedistreamNotifications.i18n.viewAll}</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
|
||
|
|
$( '.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(
|
||
|
|
'<p class="error">' + fedistreamNotifications.i18n.error + '</p>'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
} );
|
||
|
|
},
|
||
|
|
|
||
|
|
renderNotifications: function( notifications ) {
|
||
|
|
const container = $( '.notifications-list' );
|
||
|
|
container.empty();
|
||
|
|
|
||
|
|
if ( ! notifications || notifications.length === 0 ) {
|
||
|
|
container.html(
|
||
|
|
'<p class="empty">' + fedistreamNotifications.i18n.noNotifications + '</p>'
|
||
|
|
);
|
||
|
|
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 `
|
||
|
|
<div class="notification-item ${isRead}" data-id="${notification.id}">
|
||
|
|
<div class="notification-icon">
|
||
|
|
<span class="dashicons dashicons-${icon}"></span>
|
||
|
|
</div>
|
||
|
|
<div class="notification-content">
|
||
|
|
<div class="notification-title">${this.escapeHtml( notification.title )}</div>
|
||
|
|
<div class="notification-message">${this.escapeHtml( notification.message )}</div>
|
||
|
|
<div class="notification-time">${time}</div>
|
||
|
|
</div>
|
||
|
|
<div class="notification-actions">
|
||
|
|
<button class="delete-btn" title="Delete">
|
||
|
|
<span class="dashicons dashicons-no-alt"></span>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
${link ? `<a href="${link}" class="notification-link"></a>` : ''}
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
},
|
||
|
|
|
||
|
|
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(
|
||
|
|
'<p class="empty">' + fedistreamNotifications.i18n.noNotifications + '</p>'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
} );
|
||
|
|
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(
|
||
|
|
'<span class="fedistream-notification-count">' + count + '</span>'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
} 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 );
|