mirror of
https://github.com/moodle/moodle.git
synced 2025-04-25 10:26:17 +02:00
Merge branch 'MDL-64002-master-rebased' of git://github.com/ryanwyllie/moodle
This commit is contained in:
commit
b17040fdfc
@ -1696,6 +1696,7 @@ $string['restoreuserinfofailed'] = 'The restore process has stopped because you
|
||||
$string['restoreusersprecheck'] = 'Checking user data';
|
||||
$string['restoreusersprecheckerror'] = 'Some problems were detected when checking user data';
|
||||
$string['restricted'] = 'Restricted';
|
||||
$string['retry'] = 'Retry';
|
||||
$string['returningtosite'] = 'Returning to this web site?';
|
||||
$string['returntooriginaluser'] = 'Return to {$a}';
|
||||
$string['revert'] = 'Revert';
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -103,6 +103,8 @@ function(
|
||||
var isResetting = true;
|
||||
// If the UI is currently sending a message.
|
||||
var isSendingMessage = false;
|
||||
// A buffer of messages to send.
|
||||
var sendMessageBuffer = [];
|
||||
// These functions which will be generated when this module is
|
||||
// first called. See generateRenderFunction for details.
|
||||
var render = null;
|
||||
@ -549,8 +551,9 @@ function(
|
||||
return function() {
|
||||
var messages = viewState.messages;
|
||||
var mostRecentMessage = messages.length ? messages[messages.length - 1] : null;
|
||||
var lastTimeCreated = mostRecentMessage ? mostRecentMessage.timeCreated : null;
|
||||
|
||||
if (mostRecentMessage && !isResetting && !isSendingMessage) {
|
||||
if (lastTimeCreated && !isResetting && !isSendingMessage) {
|
||||
// There may be multiple messages with the same time created value since
|
||||
// the accuracy is only down to the second. The server will include these
|
||||
// messages in the result (since it does a >= comparison on time from) so
|
||||
@ -559,7 +562,7 @@ function(
|
||||
var ignoreMessageIds = [];
|
||||
for (var i = messages.length - 1; i >= 0; i--) {
|
||||
var message = messages[i];
|
||||
if (message.timeCreated === mostRecentMessage.timeCreated) {
|
||||
if (message.timeCreated === lastTimeCreated) {
|
||||
ignoreMessageIds.push(message.id);
|
||||
} else {
|
||||
// Since the messages are ordered in ascending order of time created
|
||||
@ -575,7 +578,7 @@ function(
|
||||
0,
|
||||
newestFirst,
|
||||
ignoreMessageIds,
|
||||
mostRecentMessage.timeCreated
|
||||
lastTimeCreated
|
||||
)
|
||||
.then(function(result) {
|
||||
if (result.messages.length) {
|
||||
@ -861,16 +864,28 @@ function(
|
||||
*/
|
||||
var deleteSelectedMessages = function() {
|
||||
var messageIds = viewState.pendingDeleteMessageIds;
|
||||
var sentMessages = viewState.messages.filter(function(message) {
|
||||
// If a message sendState is null then it means it was loaded from the server or if it's
|
||||
// set to sent then it means the user has successfully sent it in this page load.
|
||||
return messageIds.indexOf(message.id) >= 0 && (message.sendState == 'sent' || message.sendState === null);
|
||||
});
|
||||
var newState = StateManager.setLoadingConfirmAction(viewState, true);
|
||||
|
||||
render(newState);
|
||||
|
||||
var deleteMessagesPromise = null;
|
||||
var deleteMessagesPromise = $.Deferred().resolve().promise();
|
||||
|
||||
if (newState.deleteMessagesForAllUsers) {
|
||||
deleteMessagesPromise = Repository.deleteMessagesForAllUsers(viewState.loggedInUserId, messageIds);
|
||||
} else {
|
||||
deleteMessagesPromise = Repository.deleteMessages(viewState.loggedInUserId, messageIds);
|
||||
if (sentMessages.length) {
|
||||
// We only need to send a request to the server if we're trying to delete messages that
|
||||
// have successfully been sent.
|
||||
var sentMessageIds = sentMessages.map(function(message) {
|
||||
return message.id;
|
||||
});
|
||||
if (newState.deleteMessagesForAllUsers) {
|
||||
deleteMessagesPromise = Repository.deleteMessagesForAllUsers(viewState.loggedInUserId, sentMessageIds);
|
||||
} else {
|
||||
deleteMessagesPromise = Repository.deleteMessages(viewState.loggedInUserId, sentMessageIds);
|
||||
}
|
||||
}
|
||||
|
||||
return deleteMessagesPromise.then(function() {
|
||||
@ -891,7 +906,8 @@ function(
|
||||
}
|
||||
|
||||
return render(newState);
|
||||
});
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1007,39 +1023,89 @@ function(
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a message to the repository, update the statemanager publish a message send event
|
||||
* and call the renderer.
|
||||
* Send all of the messages in the buffer to the server to be created. Update the
|
||||
* UI with the newly created message information.
|
||||
*
|
||||
* @param {Number} conversationId The conversation to send to.
|
||||
* @param {String} text Text to send.
|
||||
* @return {Promise} Renderer promise.
|
||||
* This function will recursively call itself in order to make sure the buffer is
|
||||
* always being processed.
|
||||
*/
|
||||
var sendMessage = function(conversationId, text) {
|
||||
var processSendMessageBuffer = function() {
|
||||
if (isSendingMessage) {
|
||||
// We're already sending messages so nothing to do.
|
||||
return;
|
||||
}
|
||||
if (!sendMessageBuffer.length) {
|
||||
// No messages waiting to send. Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// Flag that we're processing the queue.
|
||||
isSendingMessage = true;
|
||||
var newState = StateManager.setSendingMessage(viewState, true);
|
||||
// Grab all of the messages in the buffer.
|
||||
var messagesToSend = sendMessageBuffer.slice();
|
||||
// Empty the buffer since we're processing it.
|
||||
sendMessageBuffer = [];
|
||||
var conversationId = viewState.id;
|
||||
var newConversationId = null;
|
||||
|
||||
render(newState);
|
||||
|
||||
var messagesText = messagesToSend.map(function(message) {
|
||||
return message.text;
|
||||
});
|
||||
var messageIds = messagesToSend.map(function(message) {
|
||||
return message.id;
|
||||
});
|
||||
var sendMessagePromise = null;
|
||||
var newCanDeleteMessagesForAllUsers = null;
|
||||
if (!conversationId && (viewState.type != CONVERSATION_TYPES.PUBLIC)) {
|
||||
// If it's a new private conversation then we need to use the old
|
||||
// web service function to create the conversation.
|
||||
var otherUserId = getOtherUserId();
|
||||
sendMessagePromise = Repository.sendMessageToUser(otherUserId, text)
|
||||
.then(function(message) {
|
||||
newConversationId = parseInt(message.conversationid, 10);
|
||||
newCanDeleteMessagesForAllUsers = message.candeletemessagesforallusers;
|
||||
return message;
|
||||
sendMessagePromise = Repository.sendMessagesToUser(otherUserId, messagesText)
|
||||
.then(function(messages) {
|
||||
if (messages.length) {
|
||||
newConversationId = parseInt(messages[0].conversationid, 10);
|
||||
newCanDeleteMessagesForAllUsers = messages[0].candeletemessagesforallusers;
|
||||
}
|
||||
return messages;
|
||||
});
|
||||
} else {
|
||||
sendMessagePromise = Repository.sendMessageToConversation(conversationId, text);
|
||||
sendMessagePromise = Repository.sendMessagesToConversation(conversationId, messagesText);
|
||||
}
|
||||
|
||||
sendMessagePromise.then(function(message) {
|
||||
var newState = StateManager.addMessages(viewState, [message]);
|
||||
newState = StateManager.setSendingMessage(newState, false);
|
||||
sendMessagePromise
|
||||
.then(function(messages) {
|
||||
var newMessageIds = messages.map(function(message) {
|
||||
return message.id;
|
||||
});
|
||||
var data = [];
|
||||
var selectedToRemove = [];
|
||||
var selectedToAdd = [];
|
||||
|
||||
messagesToSend.forEach(function(oldMessage, index) {
|
||||
var newMessage = messages[index];
|
||||
// Update messages expects and array of arrays where the first value
|
||||
// is the old message to update and the second value is the new values
|
||||
// to set.
|
||||
data.push([oldMessage, newMessage]);
|
||||
|
||||
if (viewState.selectedMessageIds.indexOf(oldMessage.id) >= 0) {
|
||||
// If the message was added to the "selected messages" list while it was still
|
||||
// being sent then we should update it's id in that list now to make sure future
|
||||
// actions work.
|
||||
selectedToRemove.push(oldMessage.id);
|
||||
selectedToAdd.push(newMessage.id);
|
||||
}
|
||||
});
|
||||
var newState = StateManager.updateMessages(viewState, data);
|
||||
newState = StateManager.setMessagesSendSuccessById(newState, newMessageIds);
|
||||
|
||||
if (selectedToRemove.length) {
|
||||
newState = StateManager.removeSelectedMessagesById(newState, selectedToRemove);
|
||||
}
|
||||
|
||||
if (selectedToAdd.length) {
|
||||
newState = StateManager.addSelectedMessagesById(newState, selectedToAdd);
|
||||
}
|
||||
|
||||
var conversation = formatConversationForEvent(newState);
|
||||
|
||||
if (!newState.id) {
|
||||
@ -1052,19 +1118,75 @@ function(
|
||||
newState = StateManager.setCanDeleteMessagesForAllUsers(newState, newCanDeleteMessagesForAllUsers);
|
||||
}
|
||||
|
||||
// Update the UI with the new message values from the server.
|
||||
render(newState);
|
||||
// Recurse just in case there has been more messages added to the buffer.
|
||||
isSendingMessage = false;
|
||||
processSendMessageBuffer();
|
||||
PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation);
|
||||
return;
|
||||
})
|
||||
.catch(function(error) {
|
||||
isSendingMessage = false;
|
||||
var newState = StateManager.setSendingMessage(viewState, false);
|
||||
render(newState);
|
||||
Notification.exception(error);
|
||||
.catch(function(e) {
|
||||
if (e.message) {
|
||||
var errorMessage = $.Deferred().resolve(e.message).promise();
|
||||
} else {
|
||||
var errorMessage = Str.get_string('unknownerror', 'core');
|
||||
}
|
||||
|
||||
var handleFailedMessages = function(errorMessage) {
|
||||
// We failed to create messages so remove the old messages from the pending queue
|
||||
// and update the UI to indicate that the message failed.
|
||||
var newState = StateManager.setMessagesSendFailById(viewState, messageIds, errorMessage);
|
||||
render(newState);
|
||||
isSendingMessage = false;
|
||||
processSendMessageBuffer();
|
||||
};
|
||||
|
||||
errorMessage.then(handleFailedMessages)
|
||||
.catch(function(e) {
|
||||
// Hrmm, we can't even load the error messages string! We'll have to
|
||||
// hard code something in English here if we still haven't got a message
|
||||
// to show.
|
||||
var finalError = e.message || 'Something went wrong!';
|
||||
handleFailedMessages(finalError);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Buffers messages to be sent to the server. We use a buffer here to allow the
|
||||
* user to freely input messages without blocking the interface for them.
|
||||
*
|
||||
* Instead we just queue all of their messages up and send them as fast as we can.
|
||||
*
|
||||
* @param {String} text Text to send.
|
||||
*/
|
||||
var sendMessage = function(text) {
|
||||
var id = 'temp' + Date.now();
|
||||
var message = {
|
||||
id: id,
|
||||
useridfrom: viewState.loggedInUserId,
|
||||
text: text,
|
||||
timecreated: null
|
||||
};
|
||||
var newState = StateManager.addMessages(viewState, [message]);
|
||||
render(newState);
|
||||
sendMessageBuffer.push(message);
|
||||
processSendMessageBuffer();
|
||||
};
|
||||
|
||||
/**
|
||||
* Retry sending a message that failed.
|
||||
*
|
||||
* @param {Object} message The message to send.
|
||||
*/
|
||||
var retrySendMessage = function(message) {
|
||||
var newState = StateManager.setMessagesSendPendingById(viewState, [message.id]);
|
||||
render(newState);
|
||||
sendMessageBuffer.push(message);
|
||||
processSendMessageBuffer();
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the selected messages update the statemanager and render the result.
|
||||
*
|
||||
@ -1222,7 +1344,9 @@ function(
|
||||
var text = textArea.val().trim();
|
||||
|
||||
if (text !== '') {
|
||||
sendMessage(viewState.id, text);
|
||||
sendMessage(text);
|
||||
textArea.val('');
|
||||
textArea.focus();
|
||||
}
|
||||
|
||||
data.originalEvent.preventDefault();
|
||||
@ -1249,13 +1373,37 @@ function(
|
||||
}
|
||||
|
||||
var element = target.closest(SELECTORS.MESSAGE);
|
||||
var messageId = parseInt(element.attr('data-message-id'), 10);
|
||||
var messageId = element.attr('data-message-id');
|
||||
|
||||
toggleSelectMessage(messageId);
|
||||
|
||||
data.originalEvent.preventDefault();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle retry sending of message.
|
||||
*
|
||||
* @param {Object} e Element this event handler is called on.
|
||||
* @param {Object} data Data for this event.
|
||||
*/
|
||||
var handleRetrySendMessage = function(e, data) {
|
||||
var target = $(e.target);
|
||||
var element = target.closest(SELECTORS.MESSAGE);
|
||||
var messageId = element.attr('data-message-id');
|
||||
var messages = viewState.messages.filter(function(message) {
|
||||
return message.id == messageId;
|
||||
});
|
||||
var message = messages.length ? messages[0] : null;
|
||||
|
||||
if (message) {
|
||||
retrySendMessage(message);
|
||||
}
|
||||
|
||||
data.originalEvent.preventDefault();
|
||||
data.originalEvent.stopPropagation();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel edit mode event handler.
|
||||
*
|
||||
@ -1400,7 +1548,8 @@ function(
|
||||
[SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST, generateConfirmActionHandler(acceptContactRequest)],
|
||||
[SELECTORS.ACTION_DECLINE_CONTACT_REQUEST, generateConfirmActionHandler(declineContactRequest)],
|
||||
[SELECTORS.MESSAGE, handleSelectMessage],
|
||||
[SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE, handleDeleteMessagesForAllUsersToggle]
|
||||
[SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE, handleDeleteMessagesForAllUsersToggle],
|
||||
[SELECTORS.RETRY_SEND, handleRetrySendMessage]
|
||||
];
|
||||
var footerActivateHandlers = [
|
||||
[SELECTORS.SEND_MESSAGE_BUTTON, handleSendMessage],
|
||||
@ -1516,10 +1665,14 @@ function(
|
||||
var resetState = function(body, conversationId, loggedInUserProfile) {
|
||||
// Reset all of the states back to the beginning if we're loading a new
|
||||
// conversation.
|
||||
isResetting = true;
|
||||
loadedAllMessages = false;
|
||||
messagesOffset = 0;
|
||||
newMessagesPollTimer = null;
|
||||
isRendering = false;
|
||||
renderBuffer = [];
|
||||
isResetting = true;
|
||||
isSendingMessage = false;
|
||||
sendMessageBuffer = [];
|
||||
|
||||
var loggedInUserId = loggedInUserProfile.id;
|
||||
var midnight = parseInt(body.attr('data-midnight'), 10);
|
||||
@ -1741,6 +1894,7 @@ function(
|
||||
if (isNewConversation) {
|
||||
var renderPromise = null;
|
||||
var loggedInUserProfile = getLoggedInUserProfile(body);
|
||||
|
||||
if (conversation) {
|
||||
renderPromise = resetByConversation(body, conversation, loggedInUserProfile, otherUserId);
|
||||
} else if (conversationId) {
|
||||
|
@ -63,6 +63,8 @@ define([], function() {
|
||||
DAY_MESSAGES_CONTAINER: '[data-region="day-messages-container"]',
|
||||
DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE: '[data-region="delete-messages-for-all-users-toggle"]',
|
||||
DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER: '[data-region="delete-messages-for-all-users-toggle-container"]',
|
||||
ERROR_MESSAGE_CONTAINER: '[data-region="error-message-container"]',
|
||||
ERROR_MESSAGE: '[data-region="error-message"]',
|
||||
FAVOURITE_ICON_CONTAINER: '[data-region="favourite-icon-container"]',
|
||||
FOOTER_CONTAINER: '[data-region="content-messages-footer-container"]',
|
||||
HEADER: '[data-region="header-content"]',
|
||||
@ -80,10 +82,13 @@ define([], function() {
|
||||
MORE_MESSAGES_LOADING_ICON_CONTAINER: '[data-region="more-messages-loading-icon-container"]',
|
||||
MUTED_ICON_CONTAINER: '[data-region="muted-icon-container"]',
|
||||
PLACEHOLDER_CONTAINER: '[data-region="placeholder-container"]',
|
||||
RETRY_SEND: '[data-region="retry-send"]',
|
||||
SELF_CONVERSATION_MESSAGE_CONTAINER: '[data-region="self-conversation-message-container"]',
|
||||
SEND_MESSAGE_BUTTON: '[data-action="send-message"]',
|
||||
SEND_MESSAGE_ICON_CONTAINER: '[data-region="send-icon-container"]',
|
||||
TEXT: '[data-region="text"]',
|
||||
TEXT_CONTAINER: '[data-region="text-container"]',
|
||||
TIME_CREATED: '[data-region="time-created"]',
|
||||
TITLE: '[data-region="title"]'
|
||||
};
|
||||
|
||||
|
@ -45,7 +45,8 @@ function(
|
||||
*/
|
||||
var sortMessagesByDay = function(messages, midnight) {
|
||||
var messagesByDay = messages.reduce(function(carry, message) {
|
||||
var dayTimestamp = UserDate.getUserMidnightForTimestamp(message.timeCreated, midnight);
|
||||
var timeCreated = message.timeCreated ? message.timeCreated : midnight;
|
||||
var dayTimestamp = UserDate.getUserMidnightForTimestamp(timeCreated, midnight);
|
||||
|
||||
if (carry.hasOwnProperty(dayTimestamp)) {
|
||||
carry[dayTimestamp].push(message);
|
||||
@ -145,6 +146,9 @@ function(
|
||||
* @return {Boolean} Are arrays equal.
|
||||
*/
|
||||
var isArrayEqual = function(a, b) {
|
||||
// Make shallow copies so that we don't mess with the array sorting.
|
||||
a = a.slice();
|
||||
b = b.slice();
|
||||
a.sort();
|
||||
b.sort();
|
||||
var aLength = a.length;
|
||||
@ -163,17 +167,84 @@ function(
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Do a shallow check to see if two objects appear to be equal. This should
|
||||
* only be used for pretty basic objects.
|
||||
*
|
||||
* @param {Object} a First object to compare.
|
||||
* @param {Object} b Second object to compare
|
||||
* @return {Bool}
|
||||
*/
|
||||
var isObjectEqual = function(a, b) {
|
||||
var aKeys = Object.keys(a);
|
||||
var bKeys = Object.keys(b);
|
||||
|
||||
if (aKeys.length != bKeys.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return aKeys.every(function(key) {
|
||||
var aVal = a[key];
|
||||
var bVal = b[key];
|
||||
var aType = typeof aVal;
|
||||
var bType = typeof bVal;
|
||||
aType = (aVal === null) ? 'null' : aType;
|
||||
bType = (aVal === null) ? 'null' : bType;
|
||||
aType = (aType === 'object' && Array.isArray(aType)) ? 'array' : aType;
|
||||
bType = (bType === 'object' && Array.isArray(bType)) ? 'array' : bType;
|
||||
|
||||
if (aType !== bType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (aType) {
|
||||
case 'object':
|
||||
return isObjectEqual(aVal, bVal);
|
||||
case 'array':
|
||||
return isArrayEqual(aVal, bVal);
|
||||
default:
|
||||
return a[key] == b[key];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare two messages to check if they are equal. This function only checks a subset
|
||||
* of the message properties which we know will change rather than all properties.
|
||||
*
|
||||
* @param {Object} a The first message
|
||||
* @param {Object} b The second message
|
||||
* @return {Bool}
|
||||
*/
|
||||
var isMessageEqual = function(a, b) {
|
||||
return isObjectEqual(
|
||||
{
|
||||
id: a.id,
|
||||
state: a.sendState,
|
||||
text: a.text,
|
||||
timeCreated: a.timeCreated
|
||||
},
|
||||
{
|
||||
id: b.id,
|
||||
state: b.sendState,
|
||||
text: b.text,
|
||||
timeCreated: b.timeCreated
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Build a patch based on days.
|
||||
*
|
||||
* @param {Object} current Current list current items.
|
||||
* @param {Object} daysDiff Difference between current and new.
|
||||
* @param {Array} remove List of days to remove.
|
||||
* @param {Array} add List of days to add.
|
||||
* @return {Object} Patch with elements to add and remove.
|
||||
*/
|
||||
var buildDaysPatch = function(current, daysDiff) {
|
||||
var buildDaysPatch = function(current, remove, add) {
|
||||
return {
|
||||
remove: daysDiff.missingFromB,
|
||||
add: daysDiff.missingFromA.map(function(day) {
|
||||
remove: remove,
|
||||
add: add.map(function(day) {
|
||||
// Any days left over in the "next" list weren't in the "current" list
|
||||
// so they will need to be added.
|
||||
var before = findPositionInArray(current, function(candidate) {
|
||||
@ -197,24 +268,61 @@ function(
|
||||
var buildMessagesPatch = function(matchingDays) {
|
||||
var remove = [];
|
||||
var add = [];
|
||||
var update = [];
|
||||
|
||||
// Iterate over the list of days and determine which messages in those days
|
||||
// have been changed.
|
||||
matchingDays.forEach(function(days) {
|
||||
var dayCurrent = days.a;
|
||||
var dayNext = days.b;
|
||||
var messagesDiff = diffArrays(dayCurrent.messages, dayNext.messages, function(messageCurrent, messageNext) {
|
||||
return messageCurrent.id == messageNext.id;
|
||||
});
|
||||
// Find out which messages have changed in this day. This will return a list of messages
|
||||
// from the current state that couldn't be found in the next state and a list of messages in
|
||||
// the next state which couldn't be count in the current state.
|
||||
var messagesDiff = diffArrays(dayCurrent.messages, dayNext.messages, isMessageEqual);
|
||||
// Take the two arrays (list of messages changed from dayNext and list of messages changed
|
||||
// from dayCurrent) any work out which messages have been added/removed from the list and
|
||||
// which messages were just updated.
|
||||
var patch = diffArrays(
|
||||
// The messages from dayCurrent.message that weren't in dayNext.messages.
|
||||
messagesDiff.missingFromB,
|
||||
// The messages from dayNext.message that weren't in dayCurrent.messages.
|
||||
messagesDiff.missingFromA,
|
||||
function(a, b) {
|
||||
// This function is going to determine if the messages were
|
||||
// added/removed from either list or if they were simply an updated.
|
||||
//
|
||||
// If the IDs match or it was a state change (i.e. message with a temp
|
||||
// ID goes from pending to sent and receives an actual id) then they are
|
||||
// the same message which should be an update not an add/remove.
|
||||
return a.id == b.id || (a.sendState != b.sendState && a.timeAdded == b.timeAdded);
|
||||
}
|
||||
);
|
||||
|
||||
remove = remove.concat(messagesDiff.missingFromB);
|
||||
// Any messages from the current state for this day which aren't in the next state
|
||||
// for this day (i.e. the user deleted the message) means we need to remove them from
|
||||
// the UI.
|
||||
remove = remove.concat(patch.missingFromB);
|
||||
|
||||
messagesDiff.missingFromA.forEach(function(message) {
|
||||
var before = findPositionInArray(dayCurrent.messages, function(candidate) {
|
||||
if (message.timeCreated == candidate.timeCreated) {
|
||||
return message.id < candidate.id;
|
||||
} else {
|
||||
return message.timeCreated < candidate.timeCreated;
|
||||
}
|
||||
});
|
||||
// Any messages not in the current state for this day which are in the next state
|
||||
// for this day (i.e. it's a new message) means we need to add it to the UI so work
|
||||
// out where in the list of messages it should appear (it could be a new message the
|
||||
// user has sent or older messages loaded as part of the conversation scroll back).
|
||||
patch.missingFromA.forEach(function(message) {
|
||||
// By default a null value for before will render the message at the bottom of
|
||||
// the message UI (i.e. it's the newest message).
|
||||
var before = null;
|
||||
|
||||
if (message.timeCreated) {
|
||||
// If this message has a time created then find where it sits in the list of
|
||||
// message to insert it into the correct position.
|
||||
before = findPositionInArray(dayCurrent.messages, function(candidate) {
|
||||
if (message.timeCreated == candidate.timeCreated) {
|
||||
return message.id < candidate.id;
|
||||
} else {
|
||||
return message.timeCreated < candidate.timeCreated;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
add.push({
|
||||
before: before,
|
||||
@ -222,11 +330,21 @@ function(
|
||||
day: dayCurrent
|
||||
});
|
||||
});
|
||||
|
||||
// Any message that appears in both the current state for this day and the next state
|
||||
// for this day means something in the message was updated.
|
||||
update = update.concat(patch.matches.map(function(message) {
|
||||
return {
|
||||
before: message.a,
|
||||
after: message.b
|
||||
};
|
||||
}));
|
||||
});
|
||||
|
||||
return {
|
||||
add: add,
|
||||
remove: remove
|
||||
remove: remove,
|
||||
update: update
|
||||
};
|
||||
};
|
||||
|
||||
@ -238,22 +356,24 @@ function(
|
||||
* @return {Object} Patch with days and messsages for each day.
|
||||
*/
|
||||
var buildConversationPatch = function(state, newState) {
|
||||
var oldMessageIds = state.messages.map(function(message) {
|
||||
return message.id;
|
||||
});
|
||||
var newMessageIds = newState.messages.map(function(message) {
|
||||
return message.id;
|
||||
});
|
||||
var diff = diffArrays(state.messages, newState.messages, isMessageEqual);
|
||||
|
||||
if (!isArrayEqual(oldMessageIds, newMessageIds)) {
|
||||
if (diff.missingFromA.length || diff.missingFromB.length) {
|
||||
// Some messages have changed so let's work out which ones by sorting
|
||||
// them into their respective days.
|
||||
var current = sortMessagesByDay(state.messages, state.midnight);
|
||||
var next = sortMessagesByDay(newState.messages, newState.midnight);
|
||||
// This diffs the arrays to work out if there are any missing days that need
|
||||
// to be added (i.e. we've got some new messages on a new day) or if there
|
||||
// are any days that need to be deleted (i.e. the user has deleted some old messages).
|
||||
var daysDiff = diffArrays(current, next, function(dayCurrent, dayNext) {
|
||||
return dayCurrent.timestamp == dayNext.timestamp;
|
||||
});
|
||||
|
||||
return {
|
||||
days: buildDaysPatch(current, daysDiff),
|
||||
// Handle adding or removing whole days.
|
||||
days: buildDaysPatch(current, daysDiff.missingFromB, daysDiff.missingFromA),
|
||||
// Handle updating messages that don't require adding/removing a whole day.
|
||||
messages: buildMessagesPatch(daysDiff.matches)
|
||||
};
|
||||
} else {
|
||||
@ -464,23 +584,6 @@ function(
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the messages are still being send
|
||||
*
|
||||
* @param {Object} state The current state.
|
||||
* @param {Object} newState The new state.
|
||||
* @return {Bool|Null} User Object if Object.
|
||||
*/
|
||||
var buildSendingMessage = function(state, newState) {
|
||||
if (!state.sendingMessage && newState.sendingMessage) {
|
||||
return true;
|
||||
} else if (state.sendingMessage && !newState.sendingMessage) {
|
||||
return false;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the user Object of user to be blocked if pending.
|
||||
*
|
||||
@ -1228,7 +1331,6 @@ function(
|
||||
loadingMembers: buildLoadingMembersPatch,
|
||||
loadingFirstMessages: buildLoadingFirstMessages,
|
||||
loadingMessages: buildLoadingMessages,
|
||||
sendingMessage: buildSendingMessage,
|
||||
confirmDeleteSelectedMessages: buildConfirmDeleteSelectedMessages,
|
||||
inEditMode: buildInEditMode,
|
||||
selectedMessages: buildSelectedMessages,
|
||||
|
@ -420,16 +420,6 @@ function(
|
||||
getHeaderPlaceholderContainer(header).addClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text input area element.
|
||||
*
|
||||
* @param {Object} footer Conversation footer container element.
|
||||
* @return {Object} The footer placeholder container element.
|
||||
*/
|
||||
var getMessageTextArea = function(footer) {
|
||||
return footer.find(SELECTORS.MESSAGE_TEXT_AREA);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a message element.
|
||||
*
|
||||
@ -482,59 +472,6 @@ function(
|
||||
getMoreMessagesLoadingIconContainer(body).addClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable the message controls for sending a message.
|
||||
*
|
||||
* @param {Object} footer Conversation footer container element.
|
||||
*/
|
||||
var disableSendMessage = function(footer) {
|
||||
footer.find(SELECTORS.SEND_MESSAGE_BUTTON).prop('disabled', true);
|
||||
getMessageTextArea(footer).prop('disabled', true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable the message controls for sending a message.
|
||||
*
|
||||
* @param {Object} footer Conversation footer container element.
|
||||
*/
|
||||
var enableSendMessage = function(footer) {
|
||||
footer.find(SELECTORS.SEND_MESSAGE_BUTTON).prop('disabled', false);
|
||||
getMessageTextArea(footer).prop('disabled', false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the sending message loading icon and disable sending more.
|
||||
*
|
||||
* @param {Object} footer Conversation footer container element.
|
||||
*/
|
||||
var startSendMessageLoading = function(footer) {
|
||||
disableSendMessage(footer);
|
||||
footer.find(SELECTORS.SEND_MESSAGE_ICON_CONTAINER).addClass('hidden');
|
||||
footer.find(SELECTORS.LOADING_ICON_CONTAINER).removeClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the sending message loading icon and allow sending new messages.
|
||||
*
|
||||
* @param {Object} footer Conversation footer container element.
|
||||
*/
|
||||
var stopSendMessageLoading = function(footer) {
|
||||
enableSendMessage(footer);
|
||||
footer.find(SELECTORS.SEND_MESSAGE_ICON_CONTAINER).removeClass('hidden');
|
||||
footer.find(SELECTORS.LOADING_ICON_CONTAINER).addClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear out message text input and focus the input element.
|
||||
*
|
||||
* @param {Object} footer Conversation footer container element.
|
||||
*/
|
||||
var hasSentMessage = function(footer) {
|
||||
var textArea = getMessageTextArea(footer);
|
||||
textArea.val('');
|
||||
textArea.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the confirm dialogue container element.
|
||||
*
|
||||
@ -600,7 +537,7 @@ function(
|
||||
fromloggedinuser: message.fromLoggedInUser,
|
||||
userfrom: message.userFrom,
|
||||
text: message.text,
|
||||
formattedtime: datesCache[message.timeCreated]
|
||||
formattedtime: message.timeCreated ? datesCache[message.timeCreated] : null
|
||||
};
|
||||
});
|
||||
};
|
||||
@ -681,6 +618,82 @@ function(
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update existing messages.
|
||||
*
|
||||
* @param {Object} header The header container element.
|
||||
* @param {Object} body The body container element.
|
||||
* @param {Object} footer The footer container element.
|
||||
* @param {Array} messages List of messages.
|
||||
* @param {Object} datesCache Cache timestamps and their formatted date string.
|
||||
*/
|
||||
var renderUpdateMessages = function(header, body, footer, messages, datesCache) {
|
||||
messages.forEach(function(message) {
|
||||
var before = message.before;
|
||||
var after = message.after;
|
||||
var element = getMessageElement(body, before.id);
|
||||
|
||||
if (before.id != after.id) {
|
||||
element.attr('data-message-id', after.id);
|
||||
}
|
||||
|
||||
if (before.timeCreated != after.timeCreated) {
|
||||
var formattedTime = datesCache[after.timeCreated];
|
||||
element.find(SELECTORS.LOADING_ICON_CONTAINER).addClass('hidden');
|
||||
element.find(SELECTORS.TIME_CREATED).text(formattedTime).removeClass('hidden');
|
||||
}
|
||||
|
||||
if (before.sendState != after.sendState) {
|
||||
var loading = element.find(SELECTORS.LOADING_ICON_CONTAINER);
|
||||
var time = element.find(SELECTORS.TIME_CREATED);
|
||||
var retry = element.find(SELECTORS.RETRY_SEND);
|
||||
|
||||
loading.addClass('hidden');
|
||||
loading.attr('aria-hidden', 'true');
|
||||
time.addClass('hidden');
|
||||
time.attr('aria-hidden', 'true');
|
||||
retry.addClass('hidden');
|
||||
retry.attr('aria-hidden', 'true');
|
||||
element.removeClass('border border-danger');
|
||||
|
||||
switch (after.sendState) {
|
||||
case 'pending':
|
||||
loading.removeClass('hidden');
|
||||
loading.attr('aria-hidden', 'false');
|
||||
break;
|
||||
case 'error':
|
||||
retry.removeClass('hidden');
|
||||
retry.attr('aria-hidden', 'false');
|
||||
element.addClass('border border-danger');
|
||||
break;
|
||||
case 'sent':
|
||||
time.removeClass('hidden');
|
||||
time.attr('aria-hidden', 'false');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (before.text != after.text) {
|
||||
element.find(SELECTORS.TEXT_CONTAINER).html(after.text);
|
||||
}
|
||||
|
||||
if (before.errorMessage != after.errorMessage) {
|
||||
var messageContainer = element.find(SELECTORS.ERROR_MESSAGE_CONTAINER);
|
||||
var message = messageContainer.find(SELECTORS.ERROR_MESSAGE);
|
||||
|
||||
if (after.errorMessage) {
|
||||
messageContainer.removeClass('hidden');
|
||||
messageContainer.attr('aria-hidden', 'false');
|
||||
message.text(after.errorMessage);
|
||||
} else {
|
||||
messageContainer.addClass('hidden');
|
||||
messageContainer.attr('aria-hidden', 'true');
|
||||
message.text('');
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove days from conversation.
|
||||
*
|
||||
@ -721,6 +734,7 @@ function(
|
||||
var renderingPromises = [];
|
||||
var hasAddDays = data.days.add.length > 0;
|
||||
var hasAddMessages = data.messages.add.length > 0;
|
||||
var hasUpdateMessages = data.messages.update.length > 0;
|
||||
var timestampsToFormat = [];
|
||||
var datesCachePromise = $.Deferred().resolve({}).promise();
|
||||
|
||||
@ -728,18 +742,33 @@ function(
|
||||
// Search for all of the timeCreated values in all of the messages in all of
|
||||
// the days that we need to render.
|
||||
timestampsToFormat = timestampsToFormat.concat(data.days.add.reduce(function(carry, day) {
|
||||
return carry.concat(day.value.messages.map(function(message) {
|
||||
return message.timeCreated;
|
||||
}));
|
||||
return carry.concat(day.value.messages.reduce(function(timestamps, message) {
|
||||
if (message.timeCreated) {
|
||||
timestamps.push(message.timeCreated);
|
||||
}
|
||||
return timestamps;
|
||||
}, []));
|
||||
}, []));
|
||||
}
|
||||
|
||||
if (hasAddMessages) {
|
||||
// Search for all of the timeCreated values in all of the messages that we
|
||||
// need to render.
|
||||
timestampsToFormat = timestampsToFormat.concat(data.messages.add.map(function(message) {
|
||||
return message.value.timeCreated;
|
||||
}));
|
||||
timestampsToFormat = timestampsToFormat.concat(data.messages.add.reduce(function(timestamps, message) {
|
||||
if (message.value.timeCreated) {
|
||||
timestamps.push(message.value.timeCreated);
|
||||
}
|
||||
return timestamps;
|
||||
}, []));
|
||||
}
|
||||
|
||||
if (hasUpdateMessages) {
|
||||
timestampsToFormat = timestampsToFormat.concat(data.messages.update.reduce(function(timestamps, message) {
|
||||
if (message.before.timeCreated != message.after.timeCreated) {
|
||||
timestamps.push(message.after.timeCreated);
|
||||
}
|
||||
return timestamps;
|
||||
}, []));
|
||||
}
|
||||
|
||||
if (timestampsToFormat.length) {
|
||||
@ -777,6 +806,12 @@ function(
|
||||
}));
|
||||
}
|
||||
|
||||
if (hasUpdateMessages) {
|
||||
renderingPromises.push(datesCachePromise.then(function(datesCache) {
|
||||
return renderUpdateMessages(header, body, footer, data.messages.update, datesCache);
|
||||
}));
|
||||
}
|
||||
|
||||
if (data.days.remove.length > 0) {
|
||||
renderRemoveDays(body, data.days.remove);
|
||||
}
|
||||
@ -935,23 +970,6 @@ function(
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Activate or deactivate send message controls.
|
||||
*
|
||||
* @param {Object} header The header container element.
|
||||
* @param {Object} body The body container element.
|
||||
* @param {Object} footer The footer container element.
|
||||
* @param {Bool} isSending Message sending.
|
||||
*/
|
||||
var renderSendingMessage = function(header, body, footer, isSending) {
|
||||
if (isSending) {
|
||||
startSendMessageLoading(footer);
|
||||
} else {
|
||||
stopSendMessageLoading(footer);
|
||||
hasSentMessage(footer);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a confirmation dialogue
|
||||
*
|
||||
@ -1596,7 +1614,6 @@ function(
|
||||
loadingMembers: renderLoadingMembers,
|
||||
loadingFirstMessages: renderLoadingFirstMessages,
|
||||
loadingMessages: renderLoadingMessages,
|
||||
sendingMessage: renderSendingMessage,
|
||||
isBlocked: renderIsBlocked,
|
||||
isContact: renderIsContact,
|
||||
isFavourite: renderIsFavourite,
|
||||
|
@ -59,12 +59,12 @@ define(['jquery'], function($) {
|
||||
return messages.map(function(message) {
|
||||
var fromLoggedInUser = message.useridfrom == loggedInUserId;
|
||||
return {
|
||||
id: parseInt(message.id, 10),
|
||||
isRead: message.isread,
|
||||
// Stringify the id.
|
||||
id: "" + message.id,
|
||||
fromLoggedInUser: fromLoggedInUser,
|
||||
userFrom: members[message.useridfrom],
|
||||
text: message.text,
|
||||
timeCreated: parseInt(message.timecreated, 10)
|
||||
timeCreated: message.timecreated ? parseInt(message.timecreated, 10) : null
|
||||
};
|
||||
});
|
||||
};
|
||||
@ -134,7 +134,6 @@ define(['jquery'], function($) {
|
||||
messages: [],
|
||||
hasTriedToLoadMessages: false,
|
||||
loadingMessages: true,
|
||||
sendingMessage: false,
|
||||
loadingMembers: true,
|
||||
loadingConfirmAction: false,
|
||||
pendingBlockUserIds: [],
|
||||
@ -142,6 +141,7 @@ define(['jquery'], function($) {
|
||||
pendingRemoveContactIds: [],
|
||||
pendingAddContactIds: [],
|
||||
pendingDeleteMessageIds: [],
|
||||
pendingSendMessageIds: [],
|
||||
pendingDeleteConversation: false,
|
||||
selectedMessageIds: []
|
||||
};
|
||||
@ -157,12 +157,34 @@ define(['jquery'], function($) {
|
||||
var addMessages = function(state, messages) {
|
||||
var newState = cloneState(state);
|
||||
var formattedMessages = formatMessages(messages, state.loggedInUserId, state.members);
|
||||
formattedMessages = formattedMessages.map(function(message) {
|
||||
message.sendState = null;
|
||||
message.timeAdded = Date.now();
|
||||
message.errorMessage = null;
|
||||
return message;
|
||||
});
|
||||
var allMessages = state.messages.concat(formattedMessages);
|
||||
// Sort the messages. Oldest to newest.
|
||||
allMessages.sort(function(a, b) {
|
||||
if (a.timeCreated < b.timeCreated) {
|
||||
if (a.timeCreated === null && b.timeCreated === null) {
|
||||
if (a.timeAdded < b.timeAdded) {
|
||||
return -1;
|
||||
} else if (a.timeAdded > b.timeAdded) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (a.timeCreated === null && b.timeCreated !== null) {
|
||||
// A comes after b.
|
||||
return 1;
|
||||
} else if (a.timeCreated !== null && b.timeCreated === null) {
|
||||
// A comes before b.
|
||||
return -1;
|
||||
} else if (a.timeCreated < b.timeCreated) {
|
||||
// A comes before b.
|
||||
return -1;
|
||||
} else if (a.timeCreated > b.timeCreated) {
|
||||
// A comes after b.
|
||||
return 1;
|
||||
} else if (a.id < b.id) {
|
||||
return -1;
|
||||
@ -175,7 +197,37 @@ define(['jquery'], function($) {
|
||||
|
||||
// Filter out any duplicate messages.
|
||||
newState.messages = allMessages.filter(function(message, index, sortedMessages) {
|
||||
return !index || message.id !== sortedMessages[index - 1].id;
|
||||
return !index || message.id != sortedMessages[index - 1].id;
|
||||
});
|
||||
|
||||
return newState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update existing messages.
|
||||
*
|
||||
* @param {Object} state Current state.
|
||||
* @param {Array} data 2D array of old and new messages
|
||||
* @return {Object} state.
|
||||
*/
|
||||
var updateMessages = function(state, data) {
|
||||
var newState = cloneState(state);
|
||||
var updatesById = data.reduce(function(carry, messageData) {
|
||||
var oldMessage = messageData[0];
|
||||
var newMessage = messageData[1];
|
||||
var formattedMessages = formatMessages([newMessage], state.loggedInUserId, state.members);
|
||||
var formattedMessage = formattedMessages[0];
|
||||
|
||||
carry[oldMessage.id] = formattedMessage;
|
||||
return carry;
|
||||
}, {});
|
||||
|
||||
newState.messages = newState.messages.map(function(message) {
|
||||
if (message.id in updatesById) {
|
||||
return $.extend(message, updatesById[message.id]);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
});
|
||||
|
||||
return newState;
|
||||
@ -191,7 +243,7 @@ define(['jquery'], function($) {
|
||||
var removeMessages = function(state, messages) {
|
||||
var newState = cloneState(state);
|
||||
var removeMessageIds = messages.map(function(message) {
|
||||
return message.id;
|
||||
return "" + message.id;
|
||||
});
|
||||
newState.messages = newState.messages.filter(function(message) {
|
||||
return removeMessageIds.indexOf(message.id) < 0;
|
||||
@ -204,13 +256,16 @@ define(['jquery'], function($) {
|
||||
* Remove messages from state by message id.
|
||||
*
|
||||
* @param {Object} state Current state.
|
||||
* @param {Array} messagesIds Message ids to remove from state.
|
||||
* @param {Array} messageIds Message ids to remove from state.
|
||||
* @return {Object} state New state with removed messages.
|
||||
*/
|
||||
var removeMessagesById = function(state, messagesIds) {
|
||||
var removeMessagesById = function(state, messageIds) {
|
||||
var newState = cloneState(state);
|
||||
messageIds = messageIds.map(function(id) {
|
||||
return "" + id;
|
||||
});
|
||||
newState.messages = newState.messages.filter(function(message) {
|
||||
return messagesIds.indexOf(message.id) < 0;
|
||||
return messageIds.indexOf(message.id) < 0;
|
||||
});
|
||||
|
||||
return newState;
|
||||
@ -265,19 +320,6 @@ define(['jquery'], function($) {
|
||||
return newState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the state sending message attribute.
|
||||
*
|
||||
* @param {Object} state Current state.
|
||||
* @param {Bool} value New sending message value.
|
||||
* @return {Object} New state with sending message attribute.
|
||||
*/
|
||||
var setSendingMessage = function(state, value) {
|
||||
var newState = cloneState(state);
|
||||
newState.sendingMessage = value;
|
||||
return newState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the state loading members attribute.
|
||||
*
|
||||
@ -421,6 +463,69 @@ define(['jquery'], function($) {
|
||||
return newState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the state of message to pending.
|
||||
*
|
||||
* @param {Object} state Current state.
|
||||
* @param {Array} messageIds Messages to delete.
|
||||
* @return {Object} New state with array of pending delete message ids.
|
||||
*/
|
||||
var setMessagesSendPendingById = function(state, messageIds) {
|
||||
var newState = cloneState(state);
|
||||
messageIds = messageIds.map(function(id) {
|
||||
return "" + id;
|
||||
});
|
||||
newState.messages.forEach(function(message) {
|
||||
if (messageIds.indexOf(message.id) >= 0) {
|
||||
message.sendState = 'pending';
|
||||
message.errorMessage = null;
|
||||
}
|
||||
});
|
||||
return newState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the state of message to sent.
|
||||
*
|
||||
* @param {Object} state Current state.
|
||||
* @param {Array} messageIds Messages to delete.
|
||||
* @return {Object} New state with array of pending delete message ids.
|
||||
*/
|
||||
var setMessagesSendSuccessById = function(state, messageIds) {
|
||||
var newState = cloneState(state);
|
||||
messageIds = messageIds.map(function(id) {
|
||||
return "" + id;
|
||||
});
|
||||
newState.messages.forEach(function(message) {
|
||||
if (messageIds.indexOf(message.id) >= 0) {
|
||||
message.sendState = 'sent';
|
||||
message.errorMessage = null;
|
||||
}
|
||||
});
|
||||
return newState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the state of messages to error.
|
||||
*
|
||||
* @param {Object} state Current state.
|
||||
* @param {Array} messageIds Messages to delete.
|
||||
* @return {Object} New state with array of pending delete message ids.
|
||||
*/
|
||||
var setMessagesSendFailById = function(state, messageIds, errorMessage) {
|
||||
var newState = cloneState(state);
|
||||
messageIds = messageIds.map(function(id) {
|
||||
return "" + id;
|
||||
});
|
||||
newState.messages.forEach(function(message) {
|
||||
if (messageIds.indexOf(message.id) >= 0) {
|
||||
message.sendState = 'error';
|
||||
message.errorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
return newState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the state pending block userids.
|
||||
*
|
||||
@ -496,7 +601,6 @@ define(['jquery'], function($) {
|
||||
return newState;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Update the state pending block userids.
|
||||
*
|
||||
@ -566,6 +670,9 @@ define(['jquery'], function($) {
|
||||
*/
|
||||
var removePendingDeleteMessagesById = function(state, messageIds) {
|
||||
var newState = cloneState(state);
|
||||
messageIds = messageIds.map(function(id) {
|
||||
return "" + id;
|
||||
});
|
||||
newState.pendingDeleteMessageIds = newState.pendingDeleteMessageIds.filter(function(id) {
|
||||
return messageIds.indexOf(id) < 0;
|
||||
});
|
||||
@ -581,6 +688,9 @@ define(['jquery'], function($) {
|
||||
*/
|
||||
var addSelectedMessagesById = function(state, messageIds) {
|
||||
var newState = cloneState(state);
|
||||
messageIds = messageIds.map(function(id) {
|
||||
return "" + id;
|
||||
});
|
||||
newState.selectedMessageIds = newState.selectedMessageIds.concat(messageIds);
|
||||
return newState;
|
||||
};
|
||||
@ -594,6 +704,9 @@ define(['jquery'], function($) {
|
||||
*/
|
||||
var removeSelectedMessagesById = function(state, messageIds) {
|
||||
var newState = cloneState(state);
|
||||
messageIds = messageIds.map(function(id) {
|
||||
return "" + id;
|
||||
});
|
||||
newState.selectedMessageIds = newState.selectedMessageIds.filter(function(id) {
|
||||
return messageIds.indexOf(id) < 0;
|
||||
});
|
||||
@ -695,12 +808,12 @@ define(['jquery'], function($) {
|
||||
return {
|
||||
buildInitialState: buildInitialState,
|
||||
addMessages: addMessages,
|
||||
updateMessages: updateMessages,
|
||||
removeMessages: removeMessages,
|
||||
removeMessagesById: removeMessagesById,
|
||||
addMembers: addMembers,
|
||||
removeMembers: removeMembers,
|
||||
setLoadingMessages: setLoadingMessages,
|
||||
setSendingMessage: setSendingMessage,
|
||||
setLoadingMembers: setLoadingMembers,
|
||||
setId: setId,
|
||||
setName: setName,
|
||||
@ -714,6 +827,9 @@ define(['jquery'], function($) {
|
||||
setImageUrl: setImageUrl,
|
||||
setLoadingConfirmAction: setLoadingConfirmAction,
|
||||
setPendingDeleteConversation: setPendingDeleteConversation,
|
||||
setMessagesSendPendingById: setMessagesSendPendingById,
|
||||
setMessagesSendSuccessById: setMessagesSendSuccessById,
|
||||
setMessagesSendFailById: setMessagesSendFailById,
|
||||
addPendingBlockUsersById: addPendingBlockUsersById,
|
||||
addPendingRemoveContactsById: addPendingRemoveContactsById,
|
||||
addPendingUnblockUsersById: addPendingUnblockUsersById,
|
||||
|
@ -58,14 +58,38 @@
|
||||
<h6 class="text-truncate m-0 font-weight-bold">{{userfrom.fullname}}</h6>
|
||||
</div>
|
||||
{{/fromloggedinuser}}
|
||||
|
||||
<div
|
||||
class="ml-auto small text-right text-muted"
|
||||
class="ml-auto small text-right text-muted {{^formattedtime}}hidden{{/formattedtime}}"
|
||||
style="flex-shrink: 0"
|
||||
data-region="time-created"
|
||||
>
|
||||
{{formattedtime}}
|
||||
</div>
|
||||
<div
|
||||
class="ml-auto small {{#formattedtime}}hidden{{/formattedtime}}"
|
||||
{{#formattedtime}}aria-hidden="true"{{/formattedtime}}
|
||||
{{^formattedtime}}aria-hidden="false"{{/formattedtime}}
|
||||
data-region="loading-icon-container"
|
||||
>
|
||||
{{> core/loading }}
|
||||
</div>
|
||||
<div role="alert" class="text-danger hidden small" aria-hidden="true" data-region="error-message-container">
|
||||
<div class="d-flex align-items-center">
|
||||
{{#pix}} req, core {{/pix}}
|
||||
<span class="ml-1" data-region="error-message"></span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="ml-auto btn btn-link p-0 line-height-3 hidden"
|
||||
aria-hidden="true"
|
||||
data-region="retry-send"
|
||||
title="{{#str}} retry, core {{/str}}"
|
||||
>
|
||||
<span class="text-primary small">{{#str}} retry, core {{/str}}</span>
|
||||
</button>
|
||||
<span class="hidden ml-2 small text-dark" data-region="not-selected-icon">{{#pix}} i/uncheckedcircle, core {{/pix}}</span>
|
||||
<span class="hidden ml-2 small text-primary" data-region="selected-icon">{{#pix}} i/checkedcircle, core {{/pix}}</span>
|
||||
</div>
|
||||
<div dir="auto" align="initial">{{{text}}}</div>
|
||||
<div dir="auto" align="initial" data-region="text-container">{{{text}}}</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user