Skip to content
Snippets Groups Projects

Refactor inbox_block_script library.

Files
3
@@ -3,271 +3,253 @@
* Adds JavaScript functionality to the private message inbox block.
*/
Drupal.PrivateMessageInbox = {};
Drupal.PrivateMessageInbox.updateInbox = {};
(($, Drupal, drupalSettings, window, once) => {
let container;
let updateInterval;
let loadingPrev;
let loadingNew;
((Drupal, drupalSettings, window, once) => {
/**
* Used to manually trigger Drupal's JavaScript commands.
* @param {Object} data The data.
* Private message inbox block functionality.
*/
function triggerCommands(data) {
const ajaxObject = Drupal.ajax({
url: '',
base: false,
element: false,
progress: false,
});
class PrivateMessageInboxBlock {
constructor() {
this.container = null;
this.loadingPrevInProgress = false;
this.loadingNewInProgress = false;
this.updateTimeoutId = null;
}
// Trigger any ajax commands in the response.
ajaxObject.success(data, 'success');
}
/**
* Initialize the block with default state and handlers.
*
* @param {HTMLElement} blockWrapper
* The inbox block.
*/
init(blockWrapper) {
this.container = blockWrapper;
const threadId = this.container.querySelector('.private-message-thread')
?.dataset.threadId;
if (threadId) {
Drupal.PrivateMessageUtils.setActiveThread(threadId);
}
this.attachLoadOldButton();
this.scheduleInboxUpdate();
}
/**
* Updates the inbox after an Ajax call.
*/
function updateInbox() {
if (!loadingNew) {
loadingNew = true;
/**
* Updates the inbox with new threads.
*/
updateInbox() {
if (this.loadingNewInProgress) {
return;
}
this.loadingNewInProgress = true;
const ids = {};
if (container.length > 0) {
container[0]
.querySelectorAll('.private-message-thread-inbox')
.forEach((el) => {
ids[el.dataset.threadId] = el.dataset.lastUpdate;
});
}
this.container
.querySelectorAll('.private-message-thread-inbox')
.forEach((el) => {
ids[el.dataset.threadId] = el.dataset.lastUpdate;
});
$.ajax({
Drupal.ajax({
url: drupalSettings.privateMessageInboxBlock.loadNewUrl,
method: 'POST',
data: { ids },
success(data) {
loadingNew = false;
triggerCommands(data);
if (updateInterval) {
window.setTimeout(updateInbox, updateInterval);
}
},
});
submit: { ids },
})
.execute()
.always(() => {
this.loadingNewInProgress = false;
this.scheduleInboxUpdate();
});
}
}
/**
* Reorders the inbox after an Ajax Load, to show newest threads first.
* @param {Array} threadIds The threads IDs.
* @param {Array} newThreads The new Threads.
*/
function reorderInbox(threadIds, newThreads) {
const map = {};
container[0]
.querySelectorAll(':scope > .private-message-thread-inbox')
.forEach((el) => {
map[el.dataset.threadId] = $(el);
});
threadIds.forEach((threadId) => {
if (newThreads[threadId]) {
if (map[threadId]) {
map[threadId].remove();
}
$('<div/>').html(newThreads[threadId]).contents().appendTo(container);
} else if (map[threadId]) {
container.append(map[threadId]);
/**
* Sets a timeout for inbox updates.
*/
scheduleInboxUpdate() {
if (this.updateTimeoutId) {
window.clearTimeout(this.updateTimeoutId);
}
});
Drupal.attachBehaviors(container[0]);
}
const interval =
drupalSettings.privateMessageInboxBlock.ajaxRefreshRate * 1000;
if (interval) {
this.updateTimeoutId = window.setTimeout(
() => this.updateInbox(),
interval,
);
}
}
/**
* Inserts older threads into the inbox after an Ajax load.
* @param {string} threads The threads HTML.
*/
function insertPreviousThreads(threads) {
const contents = $('<div/>').html(threads).contents();
/**
* Appends older threads to the inbox.
*
* @param {string} threadsHtml
* HTML content of threads.
*/
insertPreviousThreads(threadsHtml) {
const newNodes = Drupal.PrivateMessageUtils.parseHTML(threadsHtml);
contents.css('display', 'none').appendTo(container).slideDown(300);
Drupal.attachBehaviors(contents[0]);
}
Array.from(newNodes).forEach((node) => {
const appendedElement = this.container.appendChild(node);
/**
* Adds CSS classes to the currently selected thread.
* @param {string} threadId The thread id.
*/
function setActiveThread(threadId) {
container.find('.active-thread:first').removeClass('active-thread');
container
.find(`.private-message-thread[data-thread-id="${threadId}"]:first`)
.removeClass('unread-thread')
.addClass('active-thread');
}
Drupal.attachBehaviors(appendedElement);
Drupal.PrivateMessageSlide.toggleSlide(appendedElement, true);
});
}
/**
* Click handler for the button that loads older threads into the inbox.
* @param {Object} e The event.
*/
function loadOldThreadWatcherHandler(e) {
e.preventDefault();
/**
* Handles loading older threads.
*
* @param {Event} e
* The click event.
*/
loadOldThreads(e) {
e.preventDefault();
if (this.loadingPrevInProgress) {
return;
}
if (!loadingPrev) {
loadingPrev = true;
this.loadingPrevInProgress = true;
let oldestTimestamp;
container[0].querySelectorAll('.private-message-thread').forEach((el) => {
const timestamp = parseInt(el.dataset.lastUpdate, 10);
oldestTimestamp = !oldestTimestamp
? timestamp
: Math.min(timestamp, oldestTimestamp);
});
const oldestTimestamp = Array.from(
this.container.querySelectorAll('.private-message-thread'),
).reduce((minTime, el) => {
return Math.min(minTime, parseInt(el.dataset.lastUpdate, 10));
}, Infinity);
$.ajax({
Drupal.ajax({
url: drupalSettings.privateMessageInboxBlock.loadPrevUrl,
data: {
submit: {
timestamp: oldestTimestamp,
count: drupalSettings.privateMessageInboxBlock.threadCount,
},
success(data) {
loadingPrev = false;
triggerCommands(data);
},
});
})
.execute()
.always(() => {
this.loadingPrevInProgress = false;
});
}
}
/**
* Watches the button that loads previous threads into the inbox.
* @param {Object} context The context.
*/
function loadOlderThreadWatcher(context) {
once(
'load-older-threads-watcher',
'#load-previous-threads-button',
context,
).forEach((el) => {
el.addEventListener('click', loadOldThreadWatcherHandler);
});
}
/**
* Click Handler executed when private message threads are clicked.
*
* Loads the thread into the private message window.
* @param {Event} e The event.
*/
const inboxThreadLinkListenerHandler = (e) => {
if (Drupal.PrivateMessages) {
e.preventDefault();
const threadId = e.currentTarget.dataset.threadId;
Drupal.PrivateMessages.loadThread(threadId);
setActiveThread(threadId);
/**
* Reorders the inbox to show the newest threads first.
*
* @param {Array} threadIds
* Thread IDs in the desired order.
* @param {Array} newThreads
* HTML content of new threads keyed by thread ID.
*/
reorderInbox(threadIds, newThreads) {
const existingThreads = {};
this.container
.querySelectorAll(':scope > .private-message-thread-inbox')
.forEach((el) => {
existingThreads[el.dataset.threadId] = el;
});
threadIds.forEach((threadId) => {
if (newThreads[threadId]) {
if (existingThreads[threadId]) {
existingThreads[threadId].remove();
}
const newThreadContent = Drupal.PrivateMessageUtils.parseHTML(
newThreads[threadId],
);
Array.from(newThreadContent).forEach((child) => {
const appendedElement = this.container.appendChild(child);
Drupal.attachBehaviors(appendedElement);
});
} else if (existingThreads[threadId]) {
const appendedElement = this.container.appendChild(
existingThreads[threadId],
);
Drupal.attachBehaviors(appendedElement);
}
});
}
};
/**
* Watches private message threads for clicks, so new threads can be loaded.
* @param {Object} context The context.
*/
function inboxThreadLinkListener(context) {
once(
'inbox-thread-link-listener',
'.private-message-inbox-thread-link',
context,
).forEach((el) => {
el.addEventListener('click', inboxThreadLinkListenerHandler);
});
}
/**
* Initializes the private message inbox JavaScript.
*/
function init(context) {
$(
once(
'init-inbox-block',
'.block-private-message-inbox-block .private-message-thread--full-container',
context,
),
).each(function () {
container = $(this);
const threadId = container
.children('.private-message-thread:first')
.attr('data-thread-id');
setActiveThread(threadId);
/**
* Attaches the "Load Older Threads" button handler.
*/
attachLoadOldButton() {
if (
drupalSettings.privateMessageInboxBlock.totalThreads >
drupalSettings.privateMessageInboxBlock.itemsToShow
) {
$('<div/>', { id: 'load-previous-threads-button-wrapper' })
.append(
$('<a/>', { href: '#', id: 'load-previous-threads-button' }).text(
Drupal.t('Load Previous'),
),
)
.insertAfter(container);
loadOlderThreadWatcher(document);
}
updateInterval =
drupalSettings.privateMessageInboxBlock.ajaxRefreshRate * 1000;
if (updateInterval) {
window.setTimeout(updateInbox, updateInterval);
Drupal.privateMessageInboxPrevious.displayButton(this.container, (e) =>
this.loadOldThreads(e),
);
}
});
}
}
const privateMessageInboxBlock = new PrivateMessageInboxBlock();
/**
* Attaches the private message inbox block behavior.
*/
Drupal.behaviors.privateMessageInboxBlock = {
attach(context) {
init(context);
inboxThreadLinkListener(context);
Drupal.AjaxCommands.prototype.insertInboxOldPrivateMessageThreads = (
ajax,
response,
) => {
if (response.threads) {
insertPreviousThreads(response.threads);
}
if (!response.threads || !response.hasNext) {
$('#load-previous-threads-button')
.parent()
.slideUp(300, function () {
$(this).remove();
});
}
};
Drupal.AjaxCommands.prototype.privateMessageInboxUpdate = (
ajax,
response,
) => reorderInbox(response.threadIds, response.newThreads);
Drupal.AjaxCommands.prototype.privateMessageTriggerInboxUpdate = () =>
updateInbox();
const containerFormContext = once(
'private-message-inbox-block',
'.block-private-message-inbox-block .private-message-thread--full-container',
context,
).shift();
if (Drupal.PrivateMessages) {
Drupal.PrivateMessages.setActiveThread = (id) => setActiveThread(id);
if (!containerFormContext) {
return;
}
Drupal.PrivateMessageInbox.updateInbox = () => updateInbox();
privateMessageInboxBlock.init(containerFormContext);
},
detach(context) {
$(context)
.find('#load-previous-threads-button')
.unbind('click', loadOldThreadWatcherHandler);
$(context)
.find('.private-message-inbox-thread-link')
.unbind('click', inboxThreadLinkListenerHandler);
Drupal.privateMessageInboxPrevious.detachEventListener(
context,
privateMessageInboxBlock.loadOldThreads.bind(privateMessageInboxBlock),
);
},
};
})(jQuery, Drupal, drupalSettings, window, once);
/**
* Custom AJAX commands for private message inbox.
*
* @param {Drupal.Ajax} ajax
* The Drupal Ajax object.
* @param {object} response
* Object holding the server response.
*/
Drupal.AjaxCommands.prototype.insertInboxOldPrivateMessageThreads = (
ajax,
response,
) => {
if (response.threads) {
privateMessageInboxBlock.insertPreviousThreads(response.threads);
}
if (!response.threads || !response.hasNext) {
Drupal.privateMessageInboxPrevious.slideDownButton();
}
};
/**
* Custom AJAX command to update the inbox with new threads.
*
* @param {Drupal.Ajax} ajax
* The Drupal Ajax object.
* @param {object} response
* Object holding the server response.
*/
Drupal.AjaxCommands.prototype.privateMessageInboxUpdate = (
ajax,
response,
) => {
privateMessageInboxBlock.reorderInbox(
response.threadIds,
response.newThreads,
);
};
/**
* Custom AJAX command to trigger an inbox update.
*/
Drupal.AjaxCommands.prototype.privateMessageTriggerInboxUpdate = () => {
privateMessageInboxBlock.updateInbox();
};
})(Drupal, drupalSettings, window, once);
Loading