From cd0d5bd6216d53ddafad93a567769c1486112f64 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Tue, 3 Dec 2024 15:16:20 +0100 Subject: [PATCH 01/23] Add private_message_slide library. --- css/private_message_slide.css | 13 +++++++++ css/private_message_slide.scss | 15 ++++++++++ js/private_message_slide.js | 51 ++++++++++++++++++++++++++++++++++ private_message.libraries.yml | 10 +++++++ 4 files changed, 89 insertions(+) create mode 100644 css/private_message_slide.css create mode 100644 css/private_message_slide.scss create mode 100644 js/private_message_slide.js diff --git a/css/private_message_slide.css b/css/private_message_slide.css new file mode 100644 index 00000000..0536dac9 --- /dev/null +++ b/css/private_message_slide.css @@ -0,0 +1,13 @@ +.private-message-slide { + overflow: hidden; + transition: height 300ms ease-out, opacity 300ms ease-out; +} +.private-message-slide__collapsed { + height: 0; + opacity: 0; +} +.private-message-slide__expanded { + height: auto; + opacity: 1; +} + diff --git a/css/private_message_slide.scss b/css/private_message_slide.scss new file mode 100644 index 00000000..a6db65b0 --- /dev/null +++ b/css/private_message_slide.scss @@ -0,0 +1,15 @@ +.private-message-slide { + overflow: hidden; + transition: height 300ms ease-out, opacity 300ms ease-out; + + &__collapsed { + height: 0; + opacity: 0; + } + + &__expanded { + height: auto; + opacity: 1; + } + +} diff --git a/js/private_message_slide.js b/js/private_message_slide.js new file mode 100644 index 00000000..3de49d42 --- /dev/null +++ b/js/private_message_slide.js @@ -0,0 +1,51 @@ +/** + * @file + * + * Slide functions for Private Message module. + */ + +((Drupal) => { + Drupal.PrivateMessageSlide = Drupal.PrivateMessageSlide || {}; + + /** + * Toggles slide-up and slide-down effects. + * + * @param {HTMLElement} element - The element to toggle. + * @param {boolean} expand - True to slide down, false to slide up. + */ + Drupal.PrivateMessageSlide.toggleSlide = (element, expand) => { + // Ensure the element is valid + if (!element) { + return; + } + + // Add the base slide class if not already present + element.classList.add('private-message-slide'); + + if (expand) { + // Prepare to slide down + element.classList.remove('private-message-slide__collapsed'); + element.classList.add('private-message-slide__expanded'); + + // Dynamically set the height to the element's natural height + element.style.height = `${element.scrollHeight}px`; + + // Reset height after transition to prevent issues on resize + element.addEventListener( + 'transitionend', + () => { + element.style.height = ''; // Clear inline height to use natural height + }, + { once: true }, + ); + } else { + // Slide up + element.style.height = `${element.scrollHeight}px`; // Set the current height explicitly + requestAnimationFrame(() => { + element.classList.remove('private-message-slide__expanded'); + element.classList.add('private-message-slide__collapsed'); + element.style.height = '0'; // Collapse the height + }); + } + }; +})(Drupal); diff --git a/private_message.libraries.yml b/private_message.libraries.yml index fdc3ed30..458a7b24 100644 --- a/private_message.libraries.yml +++ b/private_message.libraries.yml @@ -10,6 +10,7 @@ inbox_block_script: - core/once - core/drupal.ajax - core/drupalSettings + - private_message/private_message_slide inbox_block_style: css: @@ -50,3 +51,12 @@ message_form: - core/once - core/drupalSettings - private_message/browser_notification + +private_message_slide: + js: + js/private_message_slide.js: { } + css: + theme: + css/private_message_slide.css: { } + dependencies: + - core/drupal -- GitLab From 9c04c8ccf9c0f0d0858d680a8de1dbe06daad3b9 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Tue, 3 Dec 2024 15:28:05 +0100 Subject: [PATCH 02/23] Fix stylelint issues. --- css/private_message_slide.css | 5 +++-- css/private_message_slide.scss | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/css/private_message_slide.css b/css/private_message_slide.css index 0536dac9..f6380e1d 100644 --- a/css/private_message_slide.css +++ b/css/private_message_slide.css @@ -1,6 +1,8 @@ .private-message-slide { overflow: hidden; - transition: height 300ms ease-out, opacity 300ms ease-out; + transition: + height 300ms ease-out, + opacity 300ms ease-out; } .private-message-slide__collapsed { height: 0; @@ -10,4 +12,3 @@ height: auto; opacity: 1; } - diff --git a/css/private_message_slide.scss b/css/private_message_slide.scss index a6db65b0..3fcc7df3 100644 --- a/css/private_message_slide.scss +++ b/css/private_message_slide.scss @@ -1,6 +1,8 @@ .private-message-slide { overflow: hidden; - transition: height 300ms ease-out, opacity 300ms ease-out; + transition: + height 300ms ease-out, + opacity 300ms ease-out; &__collapsed { height: 0; -- GitLab From 90764a8a79045c25374823bde6f051a4eabd5e30 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 08:54:41 +0100 Subject: [PATCH 03/23] Add previous functionality for inbox. --- js/private_message_inbox_previous.js | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 js/private_message_inbox_previous.js diff --git a/js/private_message_inbox_previous.js b/js/private_message_inbox_previous.js new file mode 100644 index 00000000..e4cc2d80 --- /dev/null +++ b/js/private_message_inbox_previous.js @@ -0,0 +1,65 @@ +/** + * @file + * Adds JavaScript functionality to the inbox previous button. + */ + +((Drupal) => { + + /** + * Previous functionality. + */ + Drupal.privateMessageInboxPrevious = { + wrapperId: 'load-previous-threads-button-wrapper', + buttonId: 'load-previous-threads-button', + + /** + * Display the previous button. + * + * @param {HTMLElement} blockElement - The element where the button will be added. + * @param {Function} callback - The function to call on button click. + */ + displayButton(blockElement, callback) { + blockElement.insertAdjacentHTML( + 'afterend', + `<div id="${this.wrapperId}"> + <a href="#" id="${this.buttonId}" aria-label="${Drupal.t( + 'Load previous threads', + )}">${Drupal.t('Load Previous')}</a> + </div>`, + ); + + const loadPreviousButton = document.getElementById(this.buttonId); + loadPreviousButton.addEventListener('click', callback); + }, + + /** + * Slides done the button. + */ + slideDownButton() { + const buttonWrapper = document.getElementById( + this.wrapperId, + ); + if (buttonWrapper) { + Drupal.PrivateMessageSlide.toggleSlide(buttonWrapper, false); + } + }, + + /* + * Detaches event listener. + * + * @param {Document|HTMLElement} context - The context to search for the button. + * @param {Function} callback - The function to detach from the click event. + */ + detachEventListener(context, callback) { + const button = context.querySelector('#' + this.buttonId); + if (button) { + button.removeEventListener( + 'click', + callback, + ); + } + }, + }; + +})(Drupal); + -- GitLab From 9edae5680fd03796a08032e6fde53168e7156844 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 08:57:53 +0100 Subject: [PATCH 04/23] WIP --- js/private_message_inbox_block.js | 402 ++++++++++++++---------------- private_message.libraries.yml | 1 + 2 files changed, 188 insertions(+), 215 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index 7516f295..91e48a2a 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -3,271 +3,243 @@ * Adds JavaScript functionality to the private message inbox block. */ -Drupal.PrivateMessageInbox = {}; -Drupal.PrivateMessageInbox.updateInbox = {}; - -(($, Drupal, drupalSettings, window, once) => { - let container; +((Drupal, drupalSettings, window, once) => { let updateInterval; - let loadingPrev; - let loadingNew; + let container; + let loadingPrevInProgress = false; + let loadingNewInProgress = false; /** - * Used to manually trigger Drupal's JavaScript commands. - * @param {Object} data The data. + * Utility function to create a temporary DOM container. + * @param {string} html The HTML string to parse. + * @returns {DocumentFragment} Parsed HTML content as a DocumentFragment. */ - function triggerCommands(data) { - const ajaxObject = Drupal.ajax({ - url: '', - base: false, - element: false, - progress: false, - }); - - // Trigger any ajax commands in the response. - ajaxObject.success(data, 'success'); - } + const parseHTML = (html) => { + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = html; + return document.createDocumentFragment().appendChild(tempDiv); + }; /** - * Updates the inbox after an Ajax call. + * Private message inbox block functionality. */ - function updateInbox() { - if (!loadingNew) { - loadingNew = true; + Drupal.PrivateMessageInboxBlock = { + + /** + * Updates the inbox with new threads. + */ + triggerUpdateInboxCallback() { + if (loadingNewInProgress) { + return; + } + loadingNewInProgress = true; const ids = {}; - if (container.length > 0) { - container[0] - .querySelectorAll('.private-message-thread-inbox') - .forEach((el) => { - ids[el.dataset.threadId] = el.dataset.lastUpdate; - }); - } + 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); + submit: { ids }, + }) + .execute() + .always(() => { + loadingNewInProgress = false; + if (updateInterval) { - window.setTimeout(updateInbox, updateInterval); + window.setTimeout( + Drupal.PrivateMessageInboxBlock.triggerUpdateInboxCallback, + updateInterval, + ); } - }, - }); - } - } + }); + }, - /** - * 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 = {}; + /** + * Appends older threads to the inbox. + * @param {string} threads HTML content of threads. + */ + insertPreviousThreads(threads) { + const contents = parseHTML(threads).childNodes; - container[0] - .querySelectorAll(':scope > .private-message-thread-inbox') - .forEach((el) => { - map[el.dataset.threadId] = $(el); + contents.forEach((content) => { + container.appendChild(content); }); - threadIds.forEach((threadId) => { - if (newThreads[threadId]) { - if (map[threadId]) { - map[threadId].remove(); - } + // contents.css('display', 'none').appendTo(container).slideDown(300); + // Drupal.attachBehaviors(contents[0]); + }, - $('<div/>').html(newThreads[threadId]).contents().appendTo(container); - } else if (map[threadId]) { - container.append(map[threadId]); + /** + * Handles loading older threads. + * @param {Event} e The click event. + */ + triggerLoadOldThreadCallback(e) { + e.preventDefault(); + if (loadingPrevInProgress) { + return; } - }); - - Drupal.attachBehaviors(container[0]); - } - - /** - * 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(); - contents.css('display', 'none').appendTo(container).slideDown(300); - Drupal.attachBehaviors(contents[0]); - } + loadingPrevInProgress = true; - /** - * 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'); - } - - /** - * Click handler for the button that loads older threads into the inbox. - * @param {Object} e The event. - */ - function loadOldThreadWatcherHandler(e) { - e.preventDefault(); - - if (!loadingPrev) { - loadingPrev = 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); + let oldestTimestamp = Infinity; + container.querySelectorAll('.private-message-thread').forEach((el) => { + oldestTimestamp = Math.min(oldestTimestamp, parseInt(el.dataset.lastUpdate, 10)); }); - $.ajax({ + Drupal.ajax({ url: drupalSettings.privateMessageInboxBlock.loadPrevUrl, - data: { + submit: { timestamp: oldestTimestamp, count: drupalSettings.privateMessageInboxBlock.threadCount, }, - success(data) { - loadingPrev = false; - triggerCommands(data); - }, + }) + .execute() + .always(() => { + loadingPrevInProgress = false; + }); + }, + + /** + * 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 = {}; + + container.querySelectorAll(':scope > .private-message-thread-inbox').forEach((el) => { + existingThreads[el.dataset.threadId] = el; }); - } - } - /** - * 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); - }); - } + threadIds.forEach((threadId) => { + if (newThreads[threadId]) { + if (existingThreads[threadId]) { + existingThreads[threadId].remove(); + } + const newThreadContent = parseHTML(newThreads[threadId]).childNodes; + newThreadContent.forEach((child) => container.appendChild(child)); + } else if (existingThreads[threadId]) { + container.appendChild(existingThreads[threadId]); + } + }); - /** - * 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); - } - }; + // TODO: it generates js errors + // Drupal.attachBehaviors(container); + }, - /** - * 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); - }); - } + /** + * Sets the active thread visually. + * @param {string} threadId The thread ID. + */ + setActiveThread(threadId) { + const activeThread = container.querySelector('.active-thread'); + if (activeThread) { + activeThread.classList.remove('active-thread'); + } + + const targetThread = container.querySelector( + `.private-message-thread[data-thread-id="${threadId}"]`, + ); + if (targetThread) { + targetThread.classList.remove('unread-thread'); + targetThread.classList.add('active-thread'); + } + }, + }; /** - * Initializes the private message inbox JavaScript. + * Attaches the private message inbox block behavior. */ - function init(context) { - $( - once( - 'init-inbox-block', + Drupal.behaviors.privateMessageInboxBlock = { + attach(context) { + const containerFormContext = once( + 'private-message-inbox-block', '.block-private-message-inbox-block .private-message-thread--full-container', context, - ), - ).each(function () { - container = $(this); + ).shift(); - const threadId = container - .children('.private-message-thread:first') - .attr('data-thread-id'); - setActiveThread(threadId); + if (!containerFormContext) { + return; + } + container = containerFormContext; + + const threadId = container.querySelector('.private-message-thread')?.dataset.threadId; + if (threadId) { + Drupal.PrivateMessageInboxBlock.setActiveThread(threadId); + } 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); + Drupal.privateMessageInboxPrevious.displayButton( + container, + Drupal.PrivateMessageInboxBlock.triggerLoadOldThreadCallback, + ); } + updateInterval = drupalSettings.privateMessageInboxBlock.ajaxRefreshRate * 1000; if (updateInterval) { - window.setTimeout(updateInbox, updateInterval); + window.setTimeout( + Drupal.PrivateMessageInboxBlock.triggerUpdateInboxCallback, + updateInterval, + ); } - }); - } - - 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(); - }); - } - }; + }, + detach(context) { + Drupal.privateMessageInboxPrevious.detachEventListener( + context, + Drupal.PrivateMessageInboxBlock.triggerLoadOldThreadCallback, + ); + + // Unbind the 'click' event from '.private-message-inbox-thread-link' elements + // const inboxThreadLinks = context.querySelectorAll('.private-message-inbox-thread-link'); + // inboxThreadLinks.forEach((link) => { + // link.removeEventListener('click', inboxThreadLinkListenerHandler); + // }); + }, + }; - Drupal.AjaxCommands.prototype.privateMessageInboxUpdate = ( - ajax, - response, - ) => reorderInbox(response.threadIds, response.newThreads); + /** + * Custom AJAX command to insert older threads into the inbox. + */ + Drupal.AjaxCommands.prototype.insertInboxOldPrivateMessageThreads = ( + ajax, + response, + ) => { + if (response.threads) { + Drupal.PrivateMessageInboxBlock.insertPreviousThreads(response.threads); + } - Drupal.AjaxCommands.prototype.privateMessageTriggerInboxUpdate = () => - updateInbox(); + if (!response.threads || !response.hasNext) { + Drupal.privateMessageInboxPrevious.slideDownButton(); + } + }; - if (Drupal.PrivateMessages) { - Drupal.PrivateMessages.setActiveThread = (id) => setActiveThread(id); - } + /** + * Custom AJAX command to update the inbox with new threads. + */ + Drupal.AjaxCommands.prototype.privateMessageInboxUpdate = ( + ajax, + response, + ) => { + Drupal.PrivateMessageInboxBlock.reorderInbox( + response.threadIds, + response.newThreads, + ); + }; - Drupal.PrivateMessageInbox.updateInbox = () => updateInbox(); - }, - detach(context) { - $(context) - .find('#load-previous-threads-button') - .unbind('click', loadOldThreadWatcherHandler); - $(context) - .find('.private-message-inbox-thread-link') - .unbind('click', inboxThreadLinkListenerHandler); - }, + /** + * Custom AJAX command to trigger an inbox update. + */ + Drupal.AjaxCommands.prototype.privateMessageTriggerInboxUpdate = () => { + Drupal.PrivateMessageInboxBlock.triggerUpdateInboxCallback(); }; -})(jQuery, Drupal, drupalSettings, window, once); +})(Drupal, drupalSettings, window, once); diff --git a/private_message.libraries.yml b/private_message.libraries.yml index 458a7b24..ac605702 100644 --- a/private_message.libraries.yml +++ b/private_message.libraries.yml @@ -4,6 +4,7 @@ history_api: inbox_block_script: js: + js/private_message_inbox_previous.js: {} js/private_message_inbox_block.js: {} dependencies: - core/jquery -- GitLab From 8581792faa60c2d8578d69ac7d5600da592bf9b5 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 09:13:48 +0100 Subject: [PATCH 05/23] setTimeoutForUpdateInboxCallback --- js/private_message_inbox_block.js | 33 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index 91e48a2a..974eed3b 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -4,7 +4,6 @@ */ ((Drupal, drupalSettings, window, once) => { - let updateInterval; let container; let loadingPrevInProgress = false; let loadingNewInProgress = false; @@ -49,16 +48,25 @@ .execute() .always(() => { loadingNewInProgress = false; - - if (updateInterval) { - window.setTimeout( - Drupal.PrivateMessageInboxBlock.triggerUpdateInboxCallback, - updateInterval, - ); - } + Drupal.PrivateMessageInboxBlock.setTimeoutForUpdateInboxCallback(); }); }, + /** + * Sets timeout for update inbox callback. + */ + setTimeoutForUpdateInboxCallback() { + let updateInterval = + drupalSettings.privateMessageInboxBlock.ajaxRefreshRate * 1000; + if (!updateInterval) { + return; + } + window.setTimeout( + Drupal.PrivateMessageInboxBlock.triggerUpdateInboxCallback, + updateInterval, + ); + }, + /** * Appends older threads to the inbox. * @param {string} threads HTML content of threads. @@ -184,14 +192,7 @@ ); } - updateInterval = - drupalSettings.privateMessageInboxBlock.ajaxRefreshRate * 1000; - if (updateInterval) { - window.setTimeout( - Drupal.PrivateMessageInboxBlock.triggerUpdateInboxCallback, - updateInterval, - ); - } + Drupal.PrivateMessageInboxBlock.setTimeoutForUpdateInboxCallback(); }, detach(context) { Drupal.privateMessageInboxPrevious.detachEventListener( -- GitLab From 0bf8c19c601d0f557d79632ca61f08195bd811d9 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 10:26:39 +0100 Subject: [PATCH 06/23] Add utils library --- js/private_message_inbox_block.js | 15 ++------------- js/private_message_utils.js | 25 +++++++++++++++++++++++++ private_message.libraries.yml | 27 +++++++++++++++++---------- 3 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 js/private_message_utils.js diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index 974eed3b..c29989bc 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -8,17 +8,6 @@ let loadingPrevInProgress = false; let loadingNewInProgress = false; - /** - * Utility function to create a temporary DOM container. - * @param {string} html The HTML string to parse. - * @returns {DocumentFragment} Parsed HTML content as a DocumentFragment. - */ - const parseHTML = (html) => { - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = html; - return document.createDocumentFragment().appendChild(tempDiv); - }; - /** * Private message inbox block functionality. */ @@ -72,7 +61,7 @@ * @param {string} threads HTML content of threads. */ insertPreviousThreads(threads) { - const contents = parseHTML(threads).childNodes; + const contents = Drupal.PrivateMessageUtils.parseHTML(threads).childNodes; contents.forEach((content) => { container.appendChild(content); @@ -130,7 +119,7 @@ if (existingThreads[threadId]) { existingThreads[threadId].remove(); } - const newThreadContent = parseHTML(newThreads[threadId]).childNodes; + const newThreadContent = Drupal.PrivateMessageUtils.parseHTML(newThreads[threadId]).childNodes; newThreadContent.forEach((child) => container.appendChild(child)); } else if (existingThreads[threadId]) { container.appendChild(existingThreads[threadId]); diff --git a/js/private_message_utils.js b/js/private_message_utils.js new file mode 100644 index 00000000..6599513e --- /dev/null +++ b/js/private_message_utils.js @@ -0,0 +1,25 @@ +/** + * @file + * Shared utility functions for Private Message module. + */ + +((Drupal) => { + 'use strict'; + + Drupal.PrivateMessageUtils = { + + /** + * Creates a temporary DOM container. + * + * @param {string} html The HTML string to parse. + * @returns {DocumentFragment} Parsed HTML content as a DocumentFragment. + */ + parseHTML(html) { + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = html; + return document.createDocumentFragment().appendChild(tempDiv); + }, + + }; + +})(Drupal); diff --git a/private_message.libraries.yml b/private_message.libraries.yml index ac605702..d5bac016 100644 --- a/private_message.libraries.yml +++ b/private_message.libraries.yml @@ -2,6 +2,21 @@ history_api: js: js/history_api.js: {} +utils: + js: + js/private_message_utils.js: {} + dependencies: + - core/drupal + +slide: + js: + js/private_message_slide.js: { } + css: + theme: + css/private_message_slide.css: { } + dependencies: + - core/drupal + inbox_block_script: js: js/private_message_inbox_previous.js: {} @@ -11,7 +26,8 @@ inbox_block_script: - core/once - core/drupal.ajax - core/drupalSettings - - private_message/private_message_slide + - private_message/slide + - private_message/utils inbox_block_style: css: @@ -52,12 +68,3 @@ message_form: - core/once - core/drupalSettings - private_message/browser_notification - -private_message_slide: - js: - js/private_message_slide.js: { } - css: - theme: - css/private_message_slide.css: { } - dependencies: - - core/drupal -- GitLab From f4d036a5226aa8cd96d1075444d96bee11a28869 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 10:40:36 +0100 Subject: [PATCH 07/23] Move as utils function. --- js/private_message_inbox_block.js | 21 +-------------------- js/private_message_utils.js | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index c29989bc..c42a5682 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -129,25 +129,6 @@ // TODO: it generates js errors // Drupal.attachBehaviors(container); }, - - /** - * Sets the active thread visually. - * @param {string} threadId The thread ID. - */ - setActiveThread(threadId) { - const activeThread = container.querySelector('.active-thread'); - if (activeThread) { - activeThread.classList.remove('active-thread'); - } - - const targetThread = container.querySelector( - `.private-message-thread[data-thread-id="${threadId}"]`, - ); - if (targetThread) { - targetThread.classList.remove('unread-thread'); - targetThread.classList.add('active-thread'); - } - }, }; /** @@ -168,7 +149,7 @@ const threadId = container.querySelector('.private-message-thread')?.dataset.threadId; if (threadId) { - Drupal.PrivateMessageInboxBlock.setActiveThread(threadId); + Drupal.PrivateMessageUtils.setActiveThread(threadId); } if ( diff --git a/js/private_message_utils.js b/js/private_message_utils.js index 6599513e..f2d23810 100644 --- a/js/private_message_utils.js +++ b/js/private_message_utils.js @@ -20,6 +20,24 @@ return document.createDocumentFragment().appendChild(tempDiv); }, + /** + * Sets the active thread visually. + * @param {string} threadId The thread ID. + */ + setActiveThread(threadId) { + const activeThread = document.querySelector('.private-message-thread--full-container .active-thread'); + if (activeThread) { + activeThread.classList.remove('active-thread'); + } + + const targetThread = document.querySelector( + `.private-message-thread--full-container .private-message-thread[data-thread-id="${threadId}"]`, + ); + if (targetThread) { + targetThread.classList.remove('unread-thread'); + targetThread.classList.add('active-thread'); + } + }, }; })(Drupal); -- GitLab From c5b1cf5c936326be54cc2ace218359cb8d5cd8f2 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 11:07:02 +0100 Subject: [PATCH 08/23] Add click listener. --- js/private_message_inbox_block.js | 23 ++++++++++------------- js/private_message_thread.js | 29 +++++++++++++++++++++++++++++ private_message.libraries.yml | 1 + 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index c42a5682..72640bd0 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -64,11 +64,12 @@ const contents = Drupal.PrivateMessageUtils.parseHTML(threads).childNodes; contents.forEach((content) => { - container.appendChild(content); + let newElement = container.appendChild(content); + + Drupal.attachBehaviors(newElement); }); // contents.css('display', 'none').appendTo(container).slideDown(300); - // Drupal.attachBehaviors(contents[0]); }, /** @@ -120,14 +121,16 @@ existingThreads[threadId].remove(); } const newThreadContent = Drupal.PrivateMessageUtils.parseHTML(newThreads[threadId]).childNodes; - newThreadContent.forEach((child) => container.appendChild(child)); + newThreadContent.forEach((child) => { + let newElement = container.appendChild(child); + Drupal.attachBehaviors(newElement); + }, + ); } else if (existingThreads[threadId]) { - container.appendChild(existingThreads[threadId]); + const newElement = container.appendChild(existingThreads[threadId]); + Drupal.attachBehaviors(newElement); } }); - - // TODO: it generates js errors - // Drupal.attachBehaviors(container); }, }; @@ -169,12 +172,6 @@ context, Drupal.PrivateMessageInboxBlock.triggerLoadOldThreadCallback, ); - - // Unbind the 'click' event from '.private-message-inbox-thread-link' elements - // const inboxThreadLinks = context.querySelectorAll('.private-message-inbox-thread-link'); - // inboxThreadLinks.forEach((link) => { - // link.removeEventListener('click', inboxThreadLinkListenerHandler); - // }); }, }; diff --git a/js/private_message_thread.js b/js/private_message_thread.js index 288af094..43ec3352 100644 --- a/js/private_message_thread.js +++ b/js/private_message_thread.js @@ -338,8 +338,31 @@ Drupal.PrivateMessages.threadChange = {}; } } + /** + * Click Handler executed when private message threads are clicked. + * + * Loads the thread into the private message window. + * @param {Event} e The event. + */ + function inboxThreadLinkListenerHandler (e) { + e.preventDefault(); + const threadId = e.currentTarget.dataset.threadId; + if (threadId) { + Drupal.PrivateMessages.loadThread(threadId); + Drupal.PrivateMessageUtils.setActiveThread(threadId); + } + } + Drupal.behaviors.privateMessageThread = { attach(context) { + once( + 'inbox-thread-link-listener', + '.private-message-inbox-thread-link', + context, + ).forEach((el) => { + el.addEventListener('click', inboxThreadLinkListenerHandler); + }); + init(); loadPreviousListener(context); currentThreadId = threadWrapper @@ -414,6 +437,12 @@ Drupal.PrivateMessages.threadChange = {}; $(context) .find('#load-previous-messages') .unbind('click', loadPreviousListenerHandler); + + // Unbind the 'click' event from '.private-message-inbox-thread-link' elements + const inboxThreadLinks = context.querySelectorAll('.private-message-inbox-thread-link'); + inboxThreadLinks.forEach((link) => { + link.removeEventListener('click', inboxThreadLinkListenerHandler); + }); }, }; diff --git a/private_message.libraries.yml b/private_message.libraries.yml index d5bac016..97d49e6e 100644 --- a/private_message.libraries.yml +++ b/private_message.libraries.yml @@ -55,6 +55,7 @@ private_message_thread_script: - core/drupalSettings - core/once - private_message/history_api + - private_message/utils private_message_thread_style: css: -- GitLab From eb962ba34831a3d7dfa1fefb7b2a1a9a3d8c6259 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 11:11:34 +0100 Subject: [PATCH 09/23] Impove standards. --- js/private_message_inbox_block.js | 32 +++++++++++++++++----------- js/private_message_inbox_previous.js | 18 +++++----------- js/private_message_thread.js | 8 ++++--- js/private_message_utils.js | 10 ++++----- 4 files changed, 33 insertions(+), 35 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index 72640bd0..3a4f79a4 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -12,7 +12,6 @@ * Private message inbox block functionality. */ Drupal.PrivateMessageInboxBlock = { - /** * Updates the inbox with new threads. */ @@ -45,7 +44,7 @@ * Sets timeout for update inbox callback. */ setTimeoutForUpdateInboxCallback() { - let updateInterval = + const updateInterval = drupalSettings.privateMessageInboxBlock.ajaxRefreshRate * 1000; if (!updateInterval) { return; @@ -64,7 +63,7 @@ const contents = Drupal.PrivateMessageUtils.parseHTML(threads).childNodes; contents.forEach((content) => { - let newElement = container.appendChild(content); + const newElement = container.appendChild(content); Drupal.attachBehaviors(newElement); }); @@ -86,7 +85,10 @@ let oldestTimestamp = Infinity; container.querySelectorAll('.private-message-thread').forEach((el) => { - oldestTimestamp = Math.min(oldestTimestamp, parseInt(el.dataset.lastUpdate, 10)); + oldestTimestamp = Math.min( + oldestTimestamp, + parseInt(el.dataset.lastUpdate, 10), + ); }); Drupal.ajax({ @@ -111,21 +113,24 @@ reorderInbox(threadIds, newThreads) { const existingThreads = {}; - container.querySelectorAll(':scope > .private-message-thread-inbox').forEach((el) => { - existingThreads[el.dataset.threadId] = el; - }); + 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]).childNodes; + const newThreadContent = Drupal.PrivateMessageUtils.parseHTML( + newThreads[threadId], + ).childNodes; newThreadContent.forEach((child) => { - let newElement = container.appendChild(child); - Drupal.attachBehaviors(newElement); - }, - ); + const newElement = container.appendChild(child); + Drupal.attachBehaviors(newElement); + }); } else if (existingThreads[threadId]) { const newElement = container.appendChild(existingThreads[threadId]); Drupal.attachBehaviors(newElement); @@ -150,7 +155,8 @@ } container = containerFormContext; - const threadId = container.querySelector('.private-message-thread')?.dataset.threadId; + const threadId = container.querySelector('.private-message-thread') + ?.dataset.threadId; if (threadId) { Drupal.PrivateMessageUtils.setActiveThread(threadId); } diff --git a/js/private_message_inbox_previous.js b/js/private_message_inbox_previous.js index e4cc2d80..b13b7040 100644 --- a/js/private_message_inbox_previous.js +++ b/js/private_message_inbox_previous.js @@ -4,7 +4,6 @@ */ ((Drupal) => { - /** * Previous functionality. */ @@ -23,8 +22,8 @@ 'afterend', `<div id="${this.wrapperId}"> <a href="#" id="${this.buttonId}" aria-label="${Drupal.t( - 'Load previous threads', - )}">${Drupal.t('Load Previous')}</a> + 'Load previous threads', + )}">${Drupal.t('Load Previous')}</a> </div>`, ); @@ -36,9 +35,7 @@ * Slides done the button. */ slideDownButton() { - const buttonWrapper = document.getElementById( - this.wrapperId, - ); + const buttonWrapper = document.getElementById(this.wrapperId); if (buttonWrapper) { Drupal.PrivateMessageSlide.toggleSlide(buttonWrapper, false); } @@ -51,15 +48,10 @@ * @param {Function} callback - The function to detach from the click event. */ detachEventListener(context, callback) { - const button = context.querySelector('#' + this.buttonId); + const button = context.querySelector(`#${this.buttonId}`); if (button) { - button.removeEventListener( - 'click', - callback, - ); + button.removeEventListener('click', callback); } }, }; - })(Drupal); - diff --git a/js/private_message_thread.js b/js/private_message_thread.js index 43ec3352..95922af6 100644 --- a/js/private_message_thread.js +++ b/js/private_message_thread.js @@ -344,9 +344,9 @@ Drupal.PrivateMessages.threadChange = {}; * Loads the thread into the private message window. * @param {Event} e The event. */ - function inboxThreadLinkListenerHandler (e) { + function inboxThreadLinkListenerHandler(e) { e.preventDefault(); - const threadId = e.currentTarget.dataset.threadId; + const { threadId } = e.currentTarget.dataset; if (threadId) { Drupal.PrivateMessages.loadThread(threadId); Drupal.PrivateMessageUtils.setActiveThread(threadId); @@ -439,7 +439,9 @@ Drupal.PrivateMessages.threadChange = {}; .unbind('click', loadPreviousListenerHandler); // Unbind the 'click' event from '.private-message-inbox-thread-link' elements - const inboxThreadLinks = context.querySelectorAll('.private-message-inbox-thread-link'); + const inboxThreadLinks = context.querySelectorAll( + '.private-message-inbox-thread-link', + ); inboxThreadLinks.forEach((link) => { link.removeEventListener('click', inboxThreadLinkListenerHandler); }); diff --git a/js/private_message_utils.js b/js/private_message_utils.js index f2d23810..54c3ab1d 100644 --- a/js/private_message_utils.js +++ b/js/private_message_utils.js @@ -4,15 +4,12 @@ */ ((Drupal) => { - 'use strict'; - Drupal.PrivateMessageUtils = { - /** * Creates a temporary DOM container. * * @param {string} html The HTML string to parse. - * @returns {DocumentFragment} Parsed HTML content as a DocumentFragment. + * @return {DocumentFragment} Parsed HTML content as a DocumentFragment. */ parseHTML(html) { const tempDiv = document.createElement('div'); @@ -25,7 +22,9 @@ * @param {string} threadId The thread ID. */ setActiveThread(threadId) { - const activeThread = document.querySelector('.private-message-thread--full-container .active-thread'); + const activeThread = document.querySelector( + '.private-message-thread--full-container .active-thread', + ); if (activeThread) { activeThread.classList.remove('active-thread'); } @@ -39,5 +38,4 @@ } }, }; - })(Drupal); -- GitLab From 0036635ea3fd3d85ac6617c323f1e30c7be2d101 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 12:01:58 +0100 Subject: [PATCH 10/23] Add init method --- js/private_message_inbox_block.js | 112 +++++++++++++++++------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index 3a4f79a4..f47ad748 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -7,15 +7,32 @@ let container; let loadingPrevInProgress = false; let loadingNewInProgress = false; + let updateTimeoutId = null; /** * Private message inbox block functionality. */ Drupal.PrivateMessageInboxBlock = { + /** + * Initialize the block with default state and handlers. + * + * @param {HTMLElement} blockWrapper - The inbox block. + */ + init(blockWrapper) { + container = blockWrapper; + const threadId = container.querySelector('.private-message-thread') + ?.dataset.threadId; + if (threadId) { + Drupal.PrivateMessageUtils.setActiveThread(threadId); + } + this.attachLoadOldButton(); + this.scheduleInboxUpdate(); + }, + /** * Updates the inbox with new threads. */ - triggerUpdateInboxCallback() { + updateInbox() { if (loadingNewInProgress) { return; } @@ -36,36 +53,36 @@ .execute() .always(() => { loadingNewInProgress = false; - Drupal.PrivateMessageInboxBlock.setTimeoutForUpdateInboxCallback(); + this.scheduleInboxUpdate(); }); }, /** - * Sets timeout for update inbox callback. + * Sets a timeout for inbox updates. */ - setTimeoutForUpdateInboxCallback() { - const updateInterval = + scheduleInboxUpdate() { + if (updateTimeoutId) { + window.clearTimeout(updateTimeoutId); + } + const interval = drupalSettings.privateMessageInboxBlock.ajaxRefreshRate * 1000; - if (!updateInterval) { - return; + if (interval) { + updateTimeoutId = window.setTimeout(() => this.updateInbox(), interval); } - window.setTimeout( - Drupal.PrivateMessageInboxBlock.triggerUpdateInboxCallback, - updateInterval, - ); }, /** * Appends older threads to the inbox. - * @param {string} threads HTML content of threads. + * @param {string} threadsHtml HTML content of threads. */ - insertPreviousThreads(threads) { - const contents = Drupal.PrivateMessageUtils.parseHTML(threads).childNodes; + insertPreviousThreads(threadsHtml) { + const newNodes = + Drupal.PrivateMessageUtils.parseHTML(threadsHtml).childNodes; - contents.forEach((content) => { - const newElement = container.appendChild(content); + newNodes.forEach((node) => { + const appendedElement = container.appendChild(node); - Drupal.attachBehaviors(newElement); + Drupal.attachBehaviors(appendedElement); }); // contents.css('display', 'none').appendTo(container).slideDown(300); @@ -75,7 +92,7 @@ * Handles loading older threads. * @param {Event} e The click event. */ - triggerLoadOldThreadCallback(e) { + loadOldThreads(e) { e.preventDefault(); if (loadingPrevInProgress) { return; @@ -83,13 +100,11 @@ loadingPrevInProgress = true; - let oldestTimestamp = Infinity; - container.querySelectorAll('.private-message-thread').forEach((el) => { - oldestTimestamp = Math.min( - oldestTimestamp, - parseInt(el.dataset.lastUpdate, 10), - ); - }); + const oldestTimestamp = Array.from( + container.querySelectorAll('.private-message-thread'), + ).reduce((minTime, el) => { + return Math.min(minTime, parseInt(el.dataset.lastUpdate, 10)); + }, Infinity); Drupal.ajax({ url: drupalSettings.privateMessageInboxBlock.loadPrevUrl, @@ -128,15 +143,31 @@ newThreads[threadId], ).childNodes; newThreadContent.forEach((child) => { - const newElement = container.appendChild(child); - Drupal.attachBehaviors(newElement); + const appendedElement = container.appendChild(child); + Drupal.attachBehaviors(appendedElement); }); } else if (existingThreads[threadId]) { - const newElement = container.appendChild(existingThreads[threadId]); - Drupal.attachBehaviors(newElement); + const appendedElement = container.appendChild( + existingThreads[threadId], + ); + Drupal.attachBehaviors(appendedElement); } }); }, + + /** + * Attaches the "Load Older Threads" button handler. + */ + attachLoadOldButton() { + if ( + drupalSettings.privateMessageInboxBlock.totalThreads > + drupalSettings.privateMessageInboxBlock.itemsToShow + ) { + Drupal.privateMessageInboxPrevious.displayButton(container, (e) => + this.loadOldThreads(e), + ); + } + }, }; /** @@ -153,30 +184,13 @@ if (!containerFormContext) { return; } - container = containerFormContext; - - const threadId = container.querySelector('.private-message-thread') - ?.dataset.threadId; - if (threadId) { - Drupal.PrivateMessageUtils.setActiveThread(threadId); - } - - if ( - drupalSettings.privateMessageInboxBlock.totalThreads > - drupalSettings.privateMessageInboxBlock.itemsToShow - ) { - Drupal.privateMessageInboxPrevious.displayButton( - container, - Drupal.PrivateMessageInboxBlock.triggerLoadOldThreadCallback, - ); - } - Drupal.PrivateMessageInboxBlock.setTimeoutForUpdateInboxCallback(); + Drupal.PrivateMessageInboxBlock.init(containerFormContext); }, detach(context) { Drupal.privateMessageInboxPrevious.detachEventListener( context, - Drupal.PrivateMessageInboxBlock.triggerLoadOldThreadCallback, + Drupal.PrivateMessageInboxBlock.loadOldThreads, ); }, }; @@ -214,6 +228,6 @@ * Custom AJAX command to trigger an inbox update. */ Drupal.AjaxCommands.prototype.privateMessageTriggerInboxUpdate = () => { - Drupal.PrivateMessageInboxBlock.triggerUpdateInboxCallback(); + Drupal.PrivateMessageInboxBlock.updateInbox(); }; })(Drupal, drupalSettings, window, once); -- GitLab From e5056d75936df724abfe2ea9634b7f42f376bf83 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 12:26:48 +0100 Subject: [PATCH 11/23] Update slide. --- js/private_message_inbox_block.js | 3 +-- js/private_message_slide.js | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index f47ad748..df934c78 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -83,9 +83,8 @@ const appendedElement = container.appendChild(node); Drupal.attachBehaviors(appendedElement); + Drupal.PrivateMessageSlide.toggleSlide(appendedElement, true); }); - - // contents.css('display', 'none').appendTo(container).slideDown(300); }, /** diff --git a/js/private_message_slide.js b/js/private_message_slide.js index 3de49d42..f6f86cc0 100644 --- a/js/private_message_slide.js +++ b/js/private_message_slide.js @@ -23,24 +23,16 @@ element.classList.add('private-message-slide'); if (expand) { - // Prepare to slide down - element.classList.remove('private-message-slide__collapsed'); - element.classList.add('private-message-slide__expanded'); + const height = `${element.scrollHeight}px`; + element.style.height = '0'; - // Dynamically set the height to the element's natural height - element.style.height = `${element.scrollHeight}px`; - - // Reset height after transition to prevent issues on resize - element.addEventListener( - 'transitionend', - () => { - element.style.height = ''; // Clear inline height to use natural height - }, - { once: true }, - ); + requestAnimationFrame(() => { + element.classList.remove('private-message-slide__collapsed'); + element.classList.add('private-message-slide__expanded'); + element.style.height = height; + }); } else { - // Slide up - element.style.height = `${element.scrollHeight}px`; // Set the current height explicitly + element.style.height = `${element.scrollHeight}px`; requestAnimationFrame(() => { element.classList.remove('private-message-slide__expanded'); element.classList.add('private-message-slide__collapsed'); -- GitLab From f734a4f8f3f702e4accabd6d08ca5567d94bf46e Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 13:45:48 +0100 Subject: [PATCH 12/23] Update slide. --- js/private_message_slide.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/js/private_message_slide.js b/js/private_message_slide.js index f6f86cc0..96ade947 100644 --- a/js/private_message_slide.js +++ b/js/private_message_slide.js @@ -23,20 +23,20 @@ element.classList.add('private-message-slide'); if (expand) { - const height = `${element.scrollHeight}px`; element.style.height = '0'; + element.classList.remove('private-message-slide__collapsed'); requestAnimationFrame(() => { - element.classList.remove('private-message-slide__collapsed'); element.classList.add('private-message-slide__expanded'); - element.style.height = height; + element.style.height = `${element.scrollHeight}px`; }); } else { element.style.height = `${element.scrollHeight}px`; + element.classList.remove('private-message-slide__expanded'); + requestAnimationFrame(() => { - element.classList.remove('private-message-slide__expanded'); element.classList.add('private-message-slide__collapsed'); - element.style.height = '0'; // Collapse the height + element.style.height = '0'; }); } }; -- GitLab From 391cfd1858095b5c132be2be215f920bfc2d2b80 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 14:39:43 +0100 Subject: [PATCH 13/23] Move variables to class --- js/private_message_inbox_block.js | 110 ++++++++++++++++----------- js/private_message_inbox_previous.js | 12 ++- js/private_message_slide.js | 6 +- js/private_message_utils.js | 10 ++- 4 files changed, 86 insertions(+), 52 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index df934c78..af55616a 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -4,43 +4,46 @@ */ ((Drupal, drupalSettings, window, once) => { - let container; - let loadingPrevInProgress = false; - let loadingNewInProgress = false; - let updateTimeoutId = null; - /** * Private message inbox block functionality. */ - Drupal.PrivateMessageInboxBlock = { + class PrivateMessageInboxBlock { + constructor() { + this.container = null; + this.loadingPrevInProgress = false; + this.loadingNewInProgress = false; + this.updateTimeoutId = null; + } + /** * Initialize the block with default state and handlers. * - * @param {HTMLElement} blockWrapper - The inbox block. + * @param {HTMLElement} blockWrapper + * The inbox block. */ init(blockWrapper) { - container = blockWrapper; - const threadId = container.querySelector('.private-message-thread') + 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 with new threads. */ updateInbox() { - if (loadingNewInProgress) { + if (this.loadingNewInProgress) { return; } - loadingNewInProgress = true; + this.loadingNewInProgress = true; const ids = {}; - container + this.container .querySelectorAll('.private-message-thread-inbox') .forEach((el) => { ids[el.dataset.threadId] = el.dataset.lastUpdate; @@ -52,55 +55,62 @@ }) .execute() .always(() => { - loadingNewInProgress = false; + this.loadingNewInProgress = false; this.scheduleInboxUpdate(); }); - }, + } /** * Sets a timeout for inbox updates. */ scheduleInboxUpdate() { - if (updateTimeoutId) { - window.clearTimeout(updateTimeoutId); + if (this.updateTimeoutId) { + window.clearTimeout(this.updateTimeoutId); } const interval = drupalSettings.privateMessageInboxBlock.ajaxRefreshRate * 1000; if (interval) { - updateTimeoutId = window.setTimeout(() => this.updateInbox(), interval); + this.updateTimeoutId = window.setTimeout( + () => this.updateInbox(), + interval, + ); } - }, + } /** * Appends older threads to the inbox. - * @param {string} threadsHtml HTML content of threads. + * + * @param {string} threadsHtml + * HTML content of threads. */ insertPreviousThreads(threadsHtml) { const newNodes = Drupal.PrivateMessageUtils.parseHTML(threadsHtml).childNodes; newNodes.forEach((node) => { - const appendedElement = container.appendChild(node); + const appendedElement = this.container.appendChild(node); Drupal.attachBehaviors(appendedElement); Drupal.PrivateMessageSlide.toggleSlide(appendedElement, true); }); - }, + } /** * Handles loading older threads. - * @param {Event} e The click event. + * + * @param {Event} e + * The click event. */ loadOldThreads(e) { e.preventDefault(); - if (loadingPrevInProgress) { + if (this.loadingPrevInProgress) { return; } - loadingPrevInProgress = true; + this.loadingPrevInProgress = true; const oldestTimestamp = Array.from( - container.querySelectorAll('.private-message-thread'), + this.container.querySelectorAll('.private-message-thread'), ).reduce((minTime, el) => { return Math.min(minTime, parseInt(el.dataset.lastUpdate, 10)); }, Infinity); @@ -114,20 +124,22 @@ }) .execute() .always(() => { - loadingPrevInProgress = false; + this.loadingPrevInProgress = false; }); - }, + } /** * 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. + * @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 = {}; - container + this.container .querySelectorAll(':scope > .private-message-thread-inbox') .forEach((el) => { existingThreads[el.dataset.threadId] = el; @@ -142,17 +154,17 @@ newThreads[threadId], ).childNodes; newThreadContent.forEach((child) => { - const appendedElement = container.appendChild(child); + const appendedElement = this.container.appendChild(child); Drupal.attachBehaviors(appendedElement); }); } else if (existingThreads[threadId]) { - const appendedElement = container.appendChild( + const appendedElement = this.container.appendChild( existingThreads[threadId], ); Drupal.attachBehaviors(appendedElement); } }); - }, + } /** * Attaches the "Load Older Threads" button handler. @@ -162,12 +174,14 @@ drupalSettings.privateMessageInboxBlock.totalThreads > drupalSettings.privateMessageInboxBlock.itemsToShow ) { - Drupal.privateMessageInboxPrevious.displayButton(container, (e) => + Drupal.privateMessageInboxPrevious.displayButton(this.container, (e) => this.loadOldThreads(e), ); } - }, - }; + } + } + + const privateMessageInboxBlock = new PrivateMessageInboxBlock(); /** * Attaches the private message inbox block behavior. @@ -184,25 +198,30 @@ return; } - Drupal.PrivateMessageInboxBlock.init(containerFormContext); + privateMessageInboxBlock.init(containerFormContext); }, detach(context) { Drupal.privateMessageInboxPrevious.detachEventListener( context, - Drupal.PrivateMessageInboxBlock.loadOldThreads, + privateMessageInboxBlock.loadOldThreads.bind(privateMessageInboxBlock), ); }, }; /** - * Custom AJAX command to insert older threads into the inbox. + * 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) { - Drupal.PrivateMessageInboxBlock.insertPreviousThreads(response.threads); + privateMessageInboxBlock.insertPreviousThreads(response.threads); } if (!response.threads || !response.hasNext) { @@ -212,12 +231,17 @@ /** * 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, ) => { - Drupal.PrivateMessageInboxBlock.reorderInbox( + privateMessageInboxBlock.reorderInbox( response.threadIds, response.newThreads, ); @@ -227,6 +251,6 @@ * Custom AJAX command to trigger an inbox update. */ Drupal.AjaxCommands.prototype.privateMessageTriggerInboxUpdate = () => { - Drupal.PrivateMessageInboxBlock.updateInbox(); + privateMessageInboxBlock.updateInbox(); }; })(Drupal, drupalSettings, window, once); diff --git a/js/private_message_inbox_previous.js b/js/private_message_inbox_previous.js index b13b7040..d470b785 100644 --- a/js/private_message_inbox_previous.js +++ b/js/private_message_inbox_previous.js @@ -14,8 +14,10 @@ /** * Display the previous button. * - * @param {HTMLElement} blockElement - The element where the button will be added. - * @param {Function} callback - The function to call on button click. + * @param {HTMLElement} blockElement + * The element where the button will be added. + * @param {Function} callback + * The function to call on button click. */ displayButton(blockElement, callback) { blockElement.insertAdjacentHTML( @@ -44,8 +46,10 @@ /* * Detaches event listener. * - * @param {Document|HTMLElement} context - The context to search for the button. - * @param {Function} callback - The function to detach from the click event. + * @param {Document|HTMLElement} context + * The context to search for the button. + * @param {Function} callback + * The function to detach from the click event. */ detachEventListener(context, callback) { const button = context.querySelector(`#${this.buttonId}`); diff --git a/js/private_message_slide.js b/js/private_message_slide.js index 96ade947..dcd63076 100644 --- a/js/private_message_slide.js +++ b/js/private_message_slide.js @@ -10,8 +10,10 @@ /** * Toggles slide-up and slide-down effects. * - * @param {HTMLElement} element - The element to toggle. - * @param {boolean} expand - True to slide down, false to slide up. + * @param {HTMLElement} element + * The element to toggle. + * @param {boolean} expand + * True to slide down, false to slide up. */ Drupal.PrivateMessageSlide.toggleSlide = (element, expand) => { // Ensure the element is valid diff --git a/js/private_message_utils.js b/js/private_message_utils.js index 54c3ab1d..d8262270 100644 --- a/js/private_message_utils.js +++ b/js/private_message_utils.js @@ -8,8 +8,10 @@ /** * Creates a temporary DOM container. * - * @param {string} html The HTML string to parse. - * @return {DocumentFragment} Parsed HTML content as a DocumentFragment. + * @param {string} html + * The HTML string to parse. + * @return {DocumentFragment} + * Parsed HTML content as a DocumentFragment. */ parseHTML(html) { const tempDiv = document.createElement('div'); @@ -19,7 +21,9 @@ /** * Sets the active thread visually. - * @param {string} threadId The thread ID. + * + * @param {string} threadId + * The thread ID. */ setActiveThread(threadId) { const activeThread = document.querySelector( -- GitLab From 98b0723c05646d6cdb28c17ae04336256f81e194 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 15:07:59 +0100 Subject: [PATCH 14/23] Drop jQuery --- private_message.libraries.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/private_message.libraries.yml b/private_message.libraries.yml index 97d49e6e..77a12bff 100644 --- a/private_message.libraries.yml +++ b/private_message.libraries.yml @@ -22,7 +22,6 @@ inbox_block_script: js/private_message_inbox_previous.js: {} js/private_message_inbox_block.js: {} dependencies: - - core/jquery - core/once - core/drupal.ajax - core/drupalSettings -- GitLab From fa6afe03d61706487fb58af464e6ad61d0662127 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Wed, 4 Dec 2024 16:05:14 +0100 Subject: [PATCH 15/23] Limit potential duplicate calls. --- js/private_message_notification_block.js | 37 ++++++++++++++---------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/js/private_message_notification_block.js b/js/private_message_notification_block.js index 9cd4aeb9..2a0ce56e 100644 --- a/js/private_message_notification_block.js +++ b/js/private_message_notification_block.js @@ -4,8 +4,8 @@ */ ((Drupal, drupalSettings, window) => { - let refreshRate; let checkingCountInProgress = false; + let updateTimeoutId = null; /** * Private message notification block. @@ -87,6 +87,24 @@ pageTitle.textContent = pageTitle.textContent.replace(titlePattern, ''); } }, + + /** + * Sets a timeout for count updates. + */ + scheduleCountUpdate() { + if (updateTimeoutId) { + window.clearTimeout(updateTimeoutId); + } + + const refreshRate = + drupalSettings.privateMessageNotificationBlock.ajaxRefreshRate * 1000; + if (refreshRate) { + updateTimeoutId = window.setTimeout( + Drupal.privateMessageNotificationBlock.triggerCountCallback, + refreshRate, + ); + } + }, }; /** @@ -102,14 +120,8 @@ context, ).shift(); - refreshRate = - drupalSettings.privateMessageNotificationBlock.ajaxRefreshRate * 1000; - - if (notificationWrapper && refreshRate) { - window.setTimeout( - Drupal.privateMessageNotificationBlock.triggerCountCallback, - refreshRate, - ); + if (notificationWrapper) { + Drupal.privateMessageNotificationBlock.scheduleCountUpdate(); } }, }; @@ -131,11 +143,6 @@ ); checkingCountInProgress = false; - if (refreshRate) { - window.setTimeout( - Drupal.privateMessageNotificationBlock.triggerCountCallback, - refreshRate, - ); - } + Drupal.privateMessageNotificationBlock.scheduleCountUpdate(); }; })(Drupal, drupalSettings, window); -- GitLab From 8ce5db8942f06f0eb83497887c7de23452810735 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Thu, 5 Dec 2024 16:32:30 +0100 Subject: [PATCH 16/23] Add falling test. --- .../FunctionalJavascript/InboxBlockTest.php | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/src/FunctionalJavascript/InboxBlockTest.php b/tests/src/FunctionalJavascript/InboxBlockTest.php index 4cec4f21..65425a09 100644 --- a/tests/src/FunctionalJavascript/InboxBlockTest.php +++ b/tests/src/FunctionalJavascript/InboxBlockTest.php @@ -41,7 +41,7 @@ class InboxBlockTest extends WebDriverTestBase { public function setUp(): void { parent::setUp(); $this->attachFullNameField(); - $this->createTestingUsers(3); + $this->createTestingUsers(4); $this->threads[] = $this->createThreadWithMessages([ $this->users['a'], @@ -85,15 +85,27 @@ class InboxBlockTest extends WebDriverTestBase { * Tests load previous functionality. */ public function testLoadPrevious(): void { + // 3 more threads, together we have 5 + $this->threads[] = $this->createThreadWithMessages([ + $this->users['a'], + $this->users['d'], + ], $this->users['d']); + $this->createThreadWithMessages([ $this->users['a'], $this->users['b'], $this->users['c'], ], $this->users['c']); + $this->createThreadWithMessages([ + $this->users['a'], + $this->users['b'], + $this->users['d'], + ], $this->users['d']); + $settings = [ 'thread_count' => 1, - 'ajax_load_count' => 1, + 'ajax_load_count' => 2, 'ajax_refresh_rate' => 100, ]; $this->drupalPlaceBlock('private_message_inbox_block', $settings); @@ -105,13 +117,13 @@ class InboxBlockTest extends WebDriverTestBase { ->getPage() ->clickLink('Load Previous'); $this->assertSession()->assertWaitOnAjaxRequest(); - $this->assertEquals(2, $this->countThreads()); + $this->assertEquals(3, $this->countThreads()); $this->getSession() ->getPage() ->clickLink('Load Previous'); $this->assertSession()->assertWaitOnAjaxRequest(); - $this->assertEquals(3, $this->countThreads()); + $this->assertEquals(5, $this->countThreads()); $this->assertSession() ->pageTextNotContains('Load Previous'); } -- GitLab From 197a117a36013f302d9c258d4cabc4e98548cbe2 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Thu, 5 Dec 2024 17:19:21 +0100 Subject: [PATCH 17/23] Clone --- js/private_message_inbox_block.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index af55616a..61d2844f 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -88,7 +88,7 @@ Drupal.PrivateMessageUtils.parseHTML(threadsHtml).childNodes; newNodes.forEach((node) => { - const appendedElement = this.container.appendChild(node); + const appendedElement = this.container.appendChild(node.cloneNode(true)); Drupal.attachBehaviors(appendedElement); Drupal.PrivateMessageSlide.toggleSlide(appendedElement, true); -- GitLab From e62ab7c340b57ed15b9059e45c3b3239f1482d4d Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Thu, 5 Dec 2024 19:56:27 +0100 Subject: [PATCH 18/23] Eslint issues --- js/private_message_inbox_block.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index 61d2844f..90366ef4 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -88,7 +88,9 @@ Drupal.PrivateMessageUtils.parseHTML(threadsHtml).childNodes; newNodes.forEach((node) => { - const appendedElement = this.container.appendChild(node.cloneNode(true)); + const appendedElement = this.container.appendChild( + node.cloneNode(true), + ); Drupal.attachBehaviors(appendedElement); Drupal.PrivateMessageSlide.toggleSlide(appendedElement, true); -- GitLab From 263dd6cf82ac35d8bd4e3ac33bb762809e83d9e0 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Thu, 5 Dec 2024 20:23:10 +0100 Subject: [PATCH 19/23] Update parser. --- js/private_message_inbox_block.js | 10 ++++------ js/private_message_utils.js | 10 +++++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index 90366ef4..72132723 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -85,12 +85,10 @@ */ insertPreviousThreads(threadsHtml) { const newNodes = - Drupal.PrivateMessageUtils.parseHTML(threadsHtml).childNodes; + Drupal.PrivateMessageUtils.parseHTML(threadsHtml); - newNodes.forEach((node) => { - const appendedElement = this.container.appendChild( - node.cloneNode(true), - ); + Array.from(newNodes).forEach((node) => { + const appendedElement = this.container.appendChild(node); Drupal.attachBehaviors(appendedElement); Drupal.PrivateMessageSlide.toggleSlide(appendedElement, true); @@ -154,7 +152,7 @@ } const newThreadContent = Drupal.PrivateMessageUtils.parseHTML( newThreads[threadId], - ).childNodes; + ); newThreadContent.forEach((child) => { const appendedElement = this.container.appendChild(child); Drupal.attachBehaviors(appendedElement); diff --git a/js/private_message_utils.js b/js/private_message_utils.js index d8262270..4480cf5d 100644 --- a/js/private_message_utils.js +++ b/js/private_message_utils.js @@ -10,13 +10,17 @@ * * @param {string} html * The HTML string to parse. - * @return {DocumentFragment} - * Parsed HTML content as a DocumentFragment. + * @returns {NodeListOf<ChildNode>} + * Parsed HTML content as NodeList. */ parseHTML(html) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; - return document.createDocumentFragment().appendChild(tempDiv); + + return document + .createDocumentFragment() + .appendChild(tempDiv) + .childNodes; }, /** -- GitLab From dd97ee4cede08ff503af256beb862757da8ed417 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Thu, 5 Dec 2024 20:24:38 +0100 Subject: [PATCH 20/23] Limit to 4 --- tests/src/FunctionalJavascript/InboxBlockTest.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/src/FunctionalJavascript/InboxBlockTest.php b/tests/src/FunctionalJavascript/InboxBlockTest.php index 65425a09..bc405695 100644 --- a/tests/src/FunctionalJavascript/InboxBlockTest.php +++ b/tests/src/FunctionalJavascript/InboxBlockTest.php @@ -85,7 +85,7 @@ class InboxBlockTest extends WebDriverTestBase { * Tests load previous functionality. */ public function testLoadPrevious(): void { - // 3 more threads, together we have 5 + // 2 more threads, together we have 4. $this->threads[] = $this->createThreadWithMessages([ $this->users['a'], $this->users['d'], @@ -97,12 +97,6 @@ class InboxBlockTest extends WebDriverTestBase { $this->users['c'], ], $this->users['c']); - $this->createThreadWithMessages([ - $this->users['a'], - $this->users['b'], - $this->users['d'], - ], $this->users['d']); - $settings = [ 'thread_count' => 1, 'ajax_load_count' => 2, @@ -123,7 +117,7 @@ class InboxBlockTest extends WebDriverTestBase { ->getPage() ->clickLink('Load Previous'); $this->assertSession()->assertWaitOnAjaxRequest(); - $this->assertEquals(5, $this->countThreads()); + $this->assertEquals(4, $this->countThreads()); $this->assertSession() ->pageTextNotContains('Load Previous'); } -- GitLab From ac738cb03cfc02d1f9c2d53fb2acbce20c7abd01 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Thu, 5 Dec 2024 20:26:08 +0100 Subject: [PATCH 21/23] Fix eslint --- js/private_message_inbox_block.js | 3 +-- js/private_message_utils.js | 7 ++----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index 72132723..929ce33e 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -84,8 +84,7 @@ * HTML content of threads. */ insertPreviousThreads(threadsHtml) { - const newNodes = - Drupal.PrivateMessageUtils.parseHTML(threadsHtml); + const newNodes = Drupal.PrivateMessageUtils.parseHTML(threadsHtml); Array.from(newNodes).forEach((node) => { const appendedElement = this.container.appendChild(node); diff --git a/js/private_message_utils.js b/js/private_message_utils.js index 4480cf5d..4ecdf637 100644 --- a/js/private_message_utils.js +++ b/js/private_message_utils.js @@ -10,17 +10,14 @@ * * @param {string} html * The HTML string to parse. - * @returns {NodeListOf<ChildNode>} + * @return {NodeListOf<ChildNode>} * Parsed HTML content as NodeList. */ parseHTML(html) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; - return document - .createDocumentFragment() - .appendChild(tempDiv) - .childNodes; + return document.createDocumentFragment().appendChild(tempDiv).childNodes; }, /** -- GitLab From f0d95c045fb6bb1121144b78cdce3eeecd04d64a Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Thu, 5 Dec 2024 20:43:29 +0100 Subject: [PATCH 22/23] Improve test. --- .../FunctionalJavascript/InboxBlockTest.php | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/tests/src/FunctionalJavascript/InboxBlockTest.php b/tests/src/FunctionalJavascript/InboxBlockTest.php index bc405695..5f68a3d9 100644 --- a/tests/src/FunctionalJavascript/InboxBlockTest.php +++ b/tests/src/FunctionalJavascript/InboxBlockTest.php @@ -86,7 +86,7 @@ class InboxBlockTest extends WebDriverTestBase { */ public function testLoadPrevious(): void { // 2 more threads, together we have 4. - $this->threads[] = $this->createThreadWithMessages([ + $this->createThreadWithMessages([ $this->users['a'], $this->users['d'], ], $this->users['d']); @@ -133,7 +133,12 @@ class InboxBlockTest extends WebDriverTestBase { $this->drupalPlaceBlock('private_message_inbox_block', $settings); $this->drupalLogin($this->users['a']); - $lastThread = $this->createThreadWithMessages([ + $this->threads[] = $this->createThreadWithMessages([ + $this->users['a'], + $this->users['d'], + ], $this->users['d']); + + $this->threads[] = $this->createThreadWithMessages([ $this->users['a'], $this->users['b'], $this->users['c'], @@ -141,9 +146,23 @@ class InboxBlockTest extends WebDriverTestBase { $this->assertSession()->assertWaitOnAjaxRequest(); - $this->assertSession() - ->elementExists('css', '.private-message-thread-inbox[data-thread-id="' . $lastThread->id() . '"]'); - $this->assertEquals(3, $this->countThreads()); + $this->assertEquals(4, $this->countThreads()); + + $threadElements = $this->getSession() + ->getPage() + ->findAll('css', '.private-message-thread-inbox'); + $threadIds = []; + foreach ($threadElements as $threadElement) { + $threadIds[] = $threadElement->getAttribute('data-thread-id'); + } + + $expectedOrder = []; + foreach ($this->threads as $thread) { + $expectedOrder[] = $thread->id(); + } + $expectedOrder = array_reverse($expectedOrder); + + $this->assertEquals($expectedOrder, $threadIds, 'Threads are not in the expected order.'); } } -- GitLab From a66d4b43b213dff7b1f1710acbe4953dfc98ce09 Mon Sep 17 00:00:00 2001 From: Adrian Lorenc <adrian.lorenc@gmail.com> Date: Thu, 5 Dec 2024 20:52:55 +0100 Subject: [PATCH 23/23] Always convert to an array --- js/private_message_inbox_block.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/private_message_inbox_block.js b/js/private_message_inbox_block.js index 929ce33e..e36d9aa7 100644 --- a/js/private_message_inbox_block.js +++ b/js/private_message_inbox_block.js @@ -152,7 +152,7 @@ const newThreadContent = Drupal.PrivateMessageUtils.parseHTML( newThreads[threadId], ); - newThreadContent.forEach((child) => { + Array.from(newThreadContent).forEach((child) => { const appendedElement = this.container.appendChild(child); Drupal.attachBehaviors(appendedElement); }); -- GitLab