Skip to content
Snippets Groups Projects
Commit e89f5bd9 authored by Jay Friendly's avatar Jay Friendly
Browse files

Issue #2947739 by Jaypan: Ensuring that the nodejs room is changed when the...

Issue #2947739 by Jaypan: Ensuring that the nodejs room is changed when the user changes threads. Adding comments to code
parent 67e19394
No related branches found
No related tags found
No related merge requests found
......@@ -7,6 +7,7 @@
/*jslint white:true, this, browser:true*/
Drupal.PrivateMessages = {};
Drupal.PrivateMessages.threadChange = {};
(function ($, Drupal, drupalSettings, window) {
......@@ -14,6 +15,9 @@ Drupal.PrivateMessages = {};
var initialized, threadWrapper, currentThreadId, originalThreadId, loadingPrev, loadingNew, container, timeout, refreshRate, dimmer, loadingThread;
/**
* Triggers AJAX commands when they happen outside the Form API framework.
*/
function triggerCommands(data) {
var ajaxObject = Drupal.ajax({
url: "",
......@@ -40,42 +44,59 @@ Drupal.PrivateMessages = {};
}
}
/**
* Click handler for the button to load previous private messages.
*/
var loadPreviousListenerHandler = function (e) {
e.preventDefault();
// Ensure that a load isn't already in progress.
if (!loadingPrev) {
loadingPrev = true;
var threadId, oldestId;
// Get the thread ID.
threadId = threadWrapper.children(".private-message-thread:first").attr("data-thread-id");
// Get the ID of the oldest message. This will be used for reference to
// tell the server which messages it should send back.
container.find(".private-message").each(function () {
if (!oldestId || Number($(this).attr("data-message-id")) < oldestId) {
oldestId = Number($(this).attr("data-message-id"));
}
});
// Retrieve messages from the server with an AJAX callback.
$.ajax({
url:drupalSettings.privateMessageThread.previousMessageCheckUrl,
data: {threadid:threadId, messageid:oldestId},
success:function (data) {
loadingPrev = false;
// Trigger the AJAX commands that were returned from the server.
triggerCommands(data);
}
});
}
};
/**
* Attaches event handlers to the load previous messages button.
*/
function loadPreviousListener(context) {
$(context).find("#load-previous-messages").once("load-previous-private-messages-listener").each(function () {
$(this).click(loadPreviousListenerHandler);
});
}
/**
* Inserts new messages into the thread.
*/
function insertNewMessages(messages) {
// Render the messages as HTML, and set them to be hidden.
var html = $("<div/>").html(messages).contents().css("display", "none");
// Insert the messages into the thread.
if (drupalSettings.privateMessageThread.messageOrder === "asc") {
html.appendTo(container);
}
......@@ -83,14 +104,18 @@ Drupal.PrivateMessages = {};
html.prependTo(container);
}
// Show the messages.
html.slideDown(300);
Drupal.attachBehaviors(html[0]);
}
// Insert older messages into the thread.
function insertPreviousMessages(messages) {
// Render the messages as HTML, setting them to be hidden.
var html = $("<div/>").html(messages).contents().css("display", "none");
// Insert the messages into the thread.
if (drupalSettings.privateMessageThread.messageOrder === "asc") {
html.prependTo(container);
}
......@@ -98,33 +123,46 @@ Drupal.PrivateMessages = {};
html.appendTo(container);
}
// Show the messages.
html.slideDown(300);
Drupal.attachBehaviors(html[0]);
}
/**
* Retrieves new messages from the server.
*/
function getNewMessages() {
// Only attempt a retrieval if one is not already in progress.
if (!loadingNew) {
var threadId, newestId = 0;
loadingNew = true;
// Get the thread ID.
threadId = threadWrapper.children(".private-message-thread:first").attr("data-thread-id");
// Get the ID of the newest message. This will be used as a reference
// server side to determine which messages to return to the browser.
container.find(".private-message").each(function () {
if (Number($(this).attr("data-message-id")) > newestId) {
newestId = Number($(this).attr("data-message-id"));
}
});
// Use AJAX to request the new messages from the server.
$.ajax({
url:drupalSettings.privateMessageThread.newMessageCheckUrl,
data: {threadid:threadId, messageid:newestId},
success:function (data) {
// Trigger any AJAX commands returned with the data.
triggerCommands(data);
// Allow for new messages to be retrieved again.
loadingNew = false;
// If refreshing has not been disabled, set a timeout to check for new
// messages after a period of time.
if (refreshRate) {
// Check for new messages again.
timeout = window.setTimeout(getNewMessages, refreshRate);
......@@ -134,21 +172,35 @@ Drupal.PrivateMessages = {};
}
}
/**
* Remove the existing thread from the DOM, and insert a new one in its place.
*/
function insertThread(thread) {
var newThread, originalThread;
// Render the new thread as HTML.
newThread = $("<div/>").html(thread).find(".private-message-thread:first");
// Find the current thread in the DOM.
originalThread = threadWrapper.children(".private-message-thread:first");
// Detach any behaviors from the old thread, to prevent memory leaks.
Drupal.detachBehaviors(threadWrapper[0]);
// Insert the new thread into the DOM.
newThread.insertAfter(originalThread);
// Remove the old thread from teh DOM.
originalThread.remove();
// Attach any behaviors to the new thread.
Drupal.attachBehaviors(threadWrapper[0]);
hideDimmer();
}
/**
* Loads a thread from the server.
*/
function loadThread(threadId, pushHistory) {
// Only try loading the thread if a thread isn't already loading, and if the
// requested thread is not the current thread.
if (!loadingThread && threadId !== currentThreadId) {
loadingThread = true;
......@@ -156,6 +208,7 @@ Drupal.PrivateMessages = {};
showDimmer();
// Load the new thread from the server with AJAX.
$.ajax({
url:drupalSettings.privateMessageThread.loadThreadUrl,
data:{id:threadId},
......@@ -172,6 +225,11 @@ Drupal.PrivateMessages = {};
}
});
// The thread ID is changing. As such, we tell any other scripts that want
// to know, that the thread has changed, and what the new thread ID is.
Drupal.PrivateMessages.emitNewThreadId(threadId);
// Change the URl if using a browser that allows it.
if (pushHistory) {
Drupal.history.push({threadId:threadId}, $("title").text(), "/private_messages/" + threadId);
}
......@@ -179,17 +237,24 @@ Drupal.PrivateMessages = {};
}
function init() {
var loadPreviousButton;
if (!initialized) {
initialized = true;
// Initialize the previous button. This will be inserted into the thread.
var loadPreviousButton;
// Find the wrapper for the current thread.
threadWrapper = $(".private-message-thread").parent();
// Get the rate (in seconds) after which the server should be polled for
// new messages.
refreshRate = drupalSettings.privateMessageThread.refreshRate;
// Get the container for messages.
container = threadWrapper.find(".private-message-thread-messages:first .private-message-wrapper:first").parent();
// Create the HTML for the load previous button.
loadPreviousButton = $("<div/>", {id:"load-previous-messages-button-wrapper"}).append($("<a/>", {href:"#", id:"load-previous-messages"}).text(Drupal.t("Load Previous")));
// Insert the load previous button into the DOM.
if (drupalSettings.privateMessageThread.messageOrder === "asc") {
loadPreviousButton.addClass("load-previous-position-before").insertBefore(container);
}
......@@ -197,12 +262,16 @@ Drupal.PrivateMessages = {};
loadPreviousButton.addClass("load-previous-position-after").insertAfter(container);
}
// Get the original thread ID on page load.
originalThreadId = threadWrapper.children(".private-message-thread:first").attr("data-thread-id");
// If the refresh rate is anything above zero (zero is disabled) start the
// server polling for new messages.
if (refreshRate) {
timeout = window.setTimeout(getNewMessages, refreshRate);
}
// Set the active thread.
if (Drupal.PrivateMessages.setActiveThread) {
Drupal.PrivateMessages.setActiveThread(originalThreadId);
}
......@@ -216,6 +285,7 @@ Drupal.PrivateMessages = {};
currentThreadId = threadWrapper.children(".private-message-thread:first").attr("data-thread-id");
container = threadWrapper.find(".private-message-thread-messages:first .private-message-wrapper:first").parent();
// Ajax commands insertPrivateMessages command callback.
Drupal.AjaxCommands.prototype.insertPrivateMessages = function (ajax, response) {
// Stifles jSlint warning.
ajax = ajax;
......@@ -235,6 +305,7 @@ Drupal.PrivateMessages = {};
}
};
// Ajax commands loadNewPrivateMessages command callback.
Drupal.AjaxCommands.prototype.loadNewPrivateMessages = function () {
window.clearTimeout(timeout);
......@@ -242,6 +313,7 @@ Drupal.PrivateMessages = {};
getNewMessages();
};
// Ajax commands privateMessageInsertThread command callback.
Drupal.AjaxCommands.prototype.privateMessageInsertThread = function (ajax, response) {
// Stifle jslint warning.
ajax = ajax;
......@@ -251,19 +323,32 @@ Drupal.PrivateMessages = {};
}
};
// Lets other modules trigger the loading of a new thread into the page.
Drupal.PrivateMessages.loadThread = function (threadId) {
loadThread(threadId, true);
};
// Lets other modules trigger a retrieval of new messages from the server.
Drupal.PrivateMessages.getNewMessages = function () {
getNewMessages();
};
// Tells other modules the ID of a new thread that has been inserted into
// the page.
Drupal.PrivateMessages.emitNewThreadId = function (threadId) {
$.each(Drupal.PrivateMessages.threadChange, function (index) {
if (Drupal.PrivateMessages.threadChange[index].threadLoaded) {
Drupal.PrivateMessages.threadChange[index].threadLoaded(threadId);
}
});
};
},
detach:function (context) {
$(context).find("#load-previous-messages").unbind("click", loadPreviousListenerHandler);
}
};
// Integrates the script with the previous/next buttons in the browser.
window.onpopstate = function (e) {
if (e.state&& e.state.threadId) {
loadThread(e.state.threadId);
......
/**
* @file
* Handles Nodejs polling with the private message module.
* Client-side script to integrate private messages with Nodejs.
*/
/*global Drupal, drupalSettings, io*/
......@@ -13,8 +13,10 @@
var initialized, socket;
function init() {
// Only initialize once.
if (!initialized) {
initialized = true;
// Connect to the Nodejs server.
socket = io(drupalSettings.privateMessageNodejs.nodejsUrl + "/thread");
}
}
......@@ -23,14 +25,27 @@
attach: function () {
init();
// Listen for a connection to the remote server.
socket.on('connect', function () {
// Sign up for the room for this message.
// Sign up for the room for this thread.
socket.emit('room', drupalSettings.privateMessageThread.threadId);
});
// Listen for an emission informing of a new private message in the
// thread.
socket.on("new private message", function () {
// Fetch new messages from the server.
Drupal.PrivateMessages.getNewMessages();
});
// Set up a callback for when the user has changed threads.
Drupal.PrivateMessages.threadChange.privateMessageNodejs = {
threadLoaded: function (threadId) {
// Join the room for the new thread.
socket.emit('room', threadId);
}
};
}
};
......
/**
* @file
* Handles Nodejs polling with the private message module.
* Server-side Nodejs implementation to handle the private message module.
*/
/*global require, console*/
......@@ -13,21 +13,36 @@
var http = require('http').Server(app);
var io = require('socket.io')(http);
// The private message thread namespace. This namespace will be used to
// emit triggers that update private messages with new messages.
var threadChannel = io.of('/thread');
threadChannel.on('connection', function (socket) {
/**
* A user's thread ID. This will be used for the room IDs.
*
* @var int
*/
var threadId;
console.log('user connected');
// Each private message thread will have its own room. This ensures that
// emissions only go to other users in the room, and not across all private
// message users everywhere.
socket.on('room', function (room) {
// Set the thread ID, so that it can be used in other callbacks.
threadId = room;
console.log("joining room: " + room);
// Join the room for the given thread.
socket.join(room);
});
// Triggered when a new private message has been added to a thread.
socket.on('new private message', function () {
console.log("Sending private message to room: " + threadId);
// Tell all users in the room that a new private message is ready to be
// fetched.
threadChannel.to(threadId).emit('new private message');
});
......
......@@ -2,8 +2,9 @@ socket.io:
version: VERSION
js:
nodejs/node_modules/socket.io-client/dist/socket.io.js: {}
js/private_message_socket.js: { }
js/private_message_nodejs.js: { }
dependencies:
- core/jquery
- core/jquery.once
- core/drupalSettings
- private_message/private_message_thread
......@@ -218,6 +218,9 @@ class AjaxController extends ControllerBase implements AjaxControllerInterface {
$messages[] = $view_builder->view($message);
}
}
// Ensure the browser knows the thread ID at all times.
$messages['#attached']['drupalSettings']['privateMessageThread']['threadId'] = (int) $thread->id();
}
$response->addCommand(new PrivateMessageInsertNewMessagesCommand($this->renderer->renderRoot($messages)));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment