AutorÃa | Ultima modificación | Ver Log |
{"version":3,"file":"message_drawer_view_conversation_patcher.min.js","sources":["../src/message_drawer_view_conversation_patcher.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * This module will take 2 view states from the message_drawer_view_conversation\n * module and generate a patch that can be given to the\n * message_drawer_view_con
versation_renderer module to update the UI.\n *\n * This module should never modify either state. It's purely a read only\n * module.\n *\n * @module core_message/message_drawer_view_conversation_patcher\n * @copyright 2018 Ryan Wyllie <ryan@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/user_date',\n 'core_message/message_drawer_view_conversation_constants'\n],\nfunction(\n $,\n UserDate,\n Constants\n) {\n /**\n * Sort messages by day.\n *\n * @param {Array} messages The list of messages to sort.\n * @param {Number} midnight User's midnight timestamp.\n * @return {Array} messages sorted by day.\n */\n var sortMessagesByDay = function(messages, midnight) {\n var messagesByDay = messages.reduce(function(carry, message) {\n var timeCreated = message.timeCreated ? message.timeCreated : midnight;\n var dayTimestamp = UserDate.getUserMidnightForTimestamp(t
imeCreated, midnight);\n\n if (carry.hasOwnProperty(dayTimestamp)) {\n carry[dayTimestamp].push(message);\n } else {\n carry[dayTimestamp] = [message];\n }\n\n return carry;\n }, {});\n\n return Object.keys(messagesByDay).map(function(dayTimestamp) {\n return {\n timestamp: dayTimestamp,\n messages: messagesByDay[dayTimestamp]\n };\n });\n };\n\n /**\n * Diff 2 arrays using a match function\n *\n * @param {Array} a The first array.\n * @param {Array} b The second array.\n * @param {Function} matchFunction Function used for matching array items.\n * @return {Object} Object containing array items missing from a, array items missing from b\n * and matches\n */\n var diffArrays = function(a, b, matchFunction) {\n // Make copy of it.\n b = b.slice();\n var missingFromA = [];\n var missingFromB = []
;\n var matches = [];\n\n a.forEach(function(current) {\n var found = false;\n var index = 0;\n\n for (; index < b.length; index++) {\n var next = b[index];\n\n if (matchFunction(current, next)) {\n found = true;\n matches.push({\n a: current,\n b: next\n });\n break;\n }\n }\n\n if (found) {\n // This day has been processed so removed it from the list.\n b.splice(index, 1);\n } else {\n // If we couldn't find it in the next messages then it means\n // it needs to be added.\n missingFromB.push(current);\n }\n });\n\n missingFromA = b;\n\n return {\n missingFromA: missingFromA,\n missingFromB: missingFromB,\n matches: matches\n
};\n };\n\n /**\n * Find an element in a array based on a matching function.\n *\n * @param {array} array Array to search.\n * @param {Function} breakFunction Function to run on array item.\n * @return {*} The array item.\n */\n var findPositionInArray = function(array, breakFunction) {\n var before = null;\n\n for (var i = 0; i < array.length; i++) {\n var candidate = array[i];\n\n if (breakFunction(candidate)) {\n return candidate;\n }\n }\n\n return before;\n };\n\n /**\n * Check if 2 arrays are equal.\n *\n * @param {Array} a The first array.\n * @param {Array} b The second array.\n * @return {Boolean} Are arrays equal.\n */\n var isArrayEqual = function(a, b) {\n // Make shallow copies so that we don't mess with the array sorting.\n a = a.slice();\n b = b.slice();\n a.sort();\n b.sort();\n var aLength = a.length;
\n var bLength = b.length;\n\n if (aLength < 1 && bLength < 1) {\n return true;\n }\n\n if (aLength != bLength) {\n return false;\n }\n\n return a.every(function(item, index) {\n return item == b[index];\n });\n };\n\n /**\n * Do a shallow check to see if two objects appear to be equal. This should\n * only be used for pretty basic objects.\n *\n * @param {Object} a First object to compare.\n * @param {Object} b Second object to compare\n * @return {Bool}\n */\n var isObjectEqual = function(a, b) {\n var aKeys = Object.keys(a);\n var bKeys = Object.keys(b);\n\n if (aKeys.length != bKeys.length) {\n return false;\n }\n\n return aKeys.every(function(key) {\n var aVal = a[key];\n var bVal = b[key];\n var aType = typeof aVal;\n var bType = typeof bVal;\n aType = (aVal === null) ? 'null' : aType;\
n bType = (aVal === null) ? 'null' : bType;\n aType = (aType === 'object' && Array.isArray(aType)) ? 'array' : aType;\n bType = (bType === 'object' && Array.isArray(bType)) ? 'array' : bType;\n\n if (aType !== bType) {\n return false;\n }\n\n switch (aType) {\n case 'object':\n return isObjectEqual(aVal, bVal);\n case 'array':\n return isArrayEqual(aVal, bVal);\n default:\n return a[key] == b[key];\n }\n });\n };\n\n /**\n * Compare two messages to check if they are equal. This function only checks a subset\n * of the message properties which we know will change rather than all properties.\n *\n * @param {Object} a The first message\n * @param {Object} b The second message\n * @return {Bool}\n */\n var isMessageEqual = function(a, b) {\n return isObjectEqual(\n
{\n id: a.id,\n state: a.sendState,\n text: a.text,\n timeCreated: a.timeCreated\n },\n {\n id: b.id,\n state: b.sendState,\n text: b.text,\n timeCreated: b.timeCreated\n }\n );\n };\n\n /**\n * Build a patch based on days.\n *\n * @param {Object} current Current list current items.\n * @param {Array} remove List of days to remove.\n * @param {Array} add List of days to add.\n * @return {Object} Patch with elements to add and remove.\n */\n var buildDaysPatch = function(current, remove, add) {\n return {\n remove: remove,\n add: add.map(function(day) {\n // Any days left over in the \"next\" list weren't in the \"current\" list\n // so they will need to be added.\n var before = findPositionInArray(current, function(candidate) {\n
return day.timestamp < candidate.timestamp;\n });\n\n return {\n before: before,\n value: day\n };\n })\n };\n };\n\n /**\n * Build the messages patch for each day.\n *\n * @param {Array} matchingDays Array of old and new messages sorted by day.\n * @return {Object} patch.\n */\n var buildMessagesPatch = function(matchingDays) {\n var remove = [];\n var add = [];\n var update = [];\n\n // Iterate over the list of days and determine which messages in those days\n // have been changed.\n matchingDays.forEach(function(days) {\n var dayCurrent = days.a;\n var dayNext = days.b;\n // Find out which messages have changed in this day. This will return a list of messages\n // from the current state that couldn't be found in the next state and a list of messages in\n // the next s
tate which couldn't be count in the current state.\n var messagesDiff = diffArrays(dayCurrent.messages, dayNext.messages, isMessageEqual);\n // Take the two arrays (list of messages changed from dayNext and list of messages changed\n // from dayCurrent) any work out which messages have been added/removed from the list and\n // which messages were just updated.\n var patch = diffArrays(\n // The messages from dayCurrent.message that weren't in dayNext.messages.\n messagesDiff.missingFromB,\n // The messages from dayNext.message that weren't in dayCurrent.messages.\n messagesDiff.missingFromA,\n function(a, b) {\n // This function is going to determine if the messages were\n // added/removed from either list or if they were simply an updated.\n //\n // If the IDs match or it was a state change (i.e. message
with a temp\n // ID goes from pending to sent and receives an actual id) then they are\n // the same message which should be an update not an add/remove.\n return a.id == b.id || (a.sendState != b.sendState && a.timeAdded == b.timeAdded);\n }\n );\n\n // Any messages from the current state for this day which aren't in the next state\n // for this day (i.e. the user deleted the message) means we need to remove them from\n // the UI.\n remove = remove.concat(patch.missingFromB);\n\n // Any messages not in the current state for this day which are in the next state\n // for this day (i.e. it's a new message) means we need to add it to the UI so work\n // out where in the list of messages it should appear (it could be a new message the\n // user has sent or older messages loaded as part of the conversation scroll back).\n patch.missin
gFromA.forEach(function(message) {\n // By default a null value for before will render the message at the bottom of\n // the message UI (i.e. it's the newest message).\n var before = null;\n\n if (message.timeCreated) {\n // If this message has a time created then find where it sits in the list of\n // message to insert it into the correct position.\n before = findPositionInArray(dayCurrent.messages, function(candidate) {\n if (message.timeCreated == candidate.timeCreated) {\n return message.id < candidate.id;\n } else {\n return message.timeCreated < candidate.timeCreated;\n }\n });\n }\n\n add.push({\n before: before,\n value: message,\n day: dayCurrent\n
});\n });\n\n // Any message that appears in both the current state for this day and the next state\n // for this day means something in the message was updated.\n update = update.concat(patch.matches.map(function(message) {\n return {\n before: message.a,\n after: message.b\n };\n }));\n });\n\n return {\n add: add,\n remove: remove,\n update: update\n };\n };\n\n /**\n * Build a patch for this conversation.\n *\n * @param {Object} state The current state of this conversation.\n * @param {Object} newState The new state of this conversation.\n * @returns {Object} Patch with days and messsages for each day.\n */\n var buildConversationPatch = function(state, newState) {\n var diff = diffArrays(state.messages, newState.messages, isMessageEqual);\n\n if (diff.missingFromA.length || dif
f.missingFromB.length) {\n // Some messages have changed so let's work out which ones by sorting\n // them into their respective days.\n var current = sortMessagesByDay(state.messages, state.midnight);\n var next = sortMessagesByDay(newState.messages, newState.midnight);\n // This diffs the arrays to work out if there are any missing days that need\n // to be added (i.e. we've got some new messages on a new day) or if there\n // are any days that need to be deleted (i.e. the user has deleted some old messages).\n var daysDiff = diffArrays(current, next, function(dayCurrent, dayNext) {\n return dayCurrent.timestamp == dayNext.timestamp;\n });\n\n return {\n // Handle adding or removing whole days.\n days: buildDaysPatch(current, daysDiff.missingFromB, daysDiff.missingFromA),\n // Handle updating messages that don't require adding/removing a w
hole day.\n messages: buildMessagesPatch(daysDiff.matches)\n };\n } else {\n return null;\n }\n };\n\n /**\n * Build a patch for the header of this conversation. Check if this conversation\n * is a group conversation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} patch\n */\n var buildHeaderPatchTypePrivate = function(state, newState) {\n var requireAddContact = buildRequireAddContact(state, newState);\n var confirmContactRequest = buildConfirmContactRequest(state, newState);\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var requiresAddContact = requireAddContact && requireAddContact.show && !requireAddContact.hasMessages;\n var requiredAddContact = requireAddContact && !requireAddContact.show;\n // Render the header once we've got a user.\n
var shouldRenderHeader = !oldOtherUser && newOtherUser;\n // We should also re-render the header if the other user requires\n // being added as a contact or if they did but no longer do.\n shouldRenderHeader = shouldRenderHeader || requiresAddContact || requiredAddContact;\n // Finally, we should re-render if the other user has sent this user\n // a contact request that is waiting for approval or if it's been approved/declined.\n shouldRenderHeader = shouldRenderHeader || confirmContactRequest !== null;\n\n if (shouldRenderHeader) {\n return {\n type: Constants.CONVERSATION_TYPES.PRIVATE,\n // We can show controls if the other user doesn't require add contact\n // and we aren't waiting for this user to respond to a contact request.\n showControls: !requiresAddContact && !confirmContactRequest,\n context: {\n id: newState.id,\n name: n
ewState.name,\n subname: newState.subname,\n totalmembercount: newState.totalMemberCount,\n imageurl: newState.imageUrl,\n isfavourite: newState.isFavourite,\n ismuted: newState.isMuted,\n // Don't show favouriting if we don't have a conversation.\n showfavourite: newState.id !== null,\n userid: newOtherUser.id,\n showonlinestatus: newOtherUser.showonlinestatus,\n isonline: newOtherUser.isonline,\n isblocked: newOtherUser.isblocked,\n iscontact: newOtherUser.iscontact\n }\n };\n }\n\n return null;\n };\n\n /**\n * Build a patch for the header of this conversation. Check if this conversation\n * is a group conversation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n *
@return {Object} patch\n */\n var buildHeaderPatchTypeSelf = function(state, newState) {\n var shouldRenderHeader = (state.name === null && newState.name !== null);\n\n if (shouldRenderHeader) {\n return {\n type: Constants.CONVERSATION_TYPES.SELF,\n // Don't display the controls for the self-conversations.\n showControls: false,\n context: {\n id: newState.id,\n name: newState.name,\n subname: newState.subname,\n imageurl: newState.imageUrl,\n isfavourite: newState.isFavourite,\n // Don't show favouriting if we don't have a conversation.\n showfavourite: newState.id !== null,\n showonlinestatus: true,\n }\n };\n }\n\n return null;\n };\n\n /**\n * Build a patch for the header of this conversation. Check if this conver
sation\n * is a group conversation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} patch\n */\n var buildHeaderPatchTypePublic = function(state, newState) {\n var oldMemberCount = state.totalMemberCount;\n var newMemberCount = newState.totalMemberCount;\n\n if (oldMemberCount != newMemberCount) {\n return {\n type: Constants.CONVERSATION_TYPES.PUBLIC,\n showControls: true,\n context: {\n id: newState.id,\n name: newState.name,\n subname: newState.subname,\n totalmembercount: newState.totalMemberCount,\n imageurl: newState.imageUrl,\n isfavourite: newState.isFavourite,\n ismuted: newState.isMuted,\n // Don't show favouriting if we don't have a conversation.\n showfavourit
e: newState.id !== null\n }\n };\n } else {\n return null;\n }\n };\n\n /**\n * Find the newest or oldest message.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Number} Oldest or newest message id.\n */\n var buildScrollToMessagePatch = function(state, newState) {\n var oldMessages = state.messages;\n var newMessages = newState.messages;\n\n if (newMessages.length < 1) {\n return null;\n }\n\n if (oldMessages.length < 1) {\n return newMessages[newMessages.length - 1].id;\n }\n\n var previousNewest = oldMessages[state.messages.length - 1];\n var currentNewest = newMessages[newMessages.length - 1];\n var previousOldest = oldMessages[0];\n var currentOldest = newMessages[0];\n\n if (previousNewest.id != currentNewest.id) {\n return currentNewest.id;\n }
else if (previousOldest.id != currentOldest.id) {\n return previousOldest.id;\n }\n\n return null;\n };\n\n /**\n * Check if members should be loaded.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildLoadingMembersPatch = function(state, newState) {\n if (!state.loadingMembers && newState.loadingMembers) {\n return true;\n } else if (state.loadingMembers && !newState.loadingMembers) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Check if the messages are being loaded for the first time.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildLoadingFirstMessages = function(state, newState) {\n if (state.hasTriedToLoadMessages === newState.hasTriedToLoadMessages) {\n
return null;\n } else if (!newState.hasTriedToLoadMessages && newState.loadingMessages) {\n return true;\n } else if (newState.hasTriedToLoadMessages && !newState.loadingMessages) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Check if the messages are still being loaded\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildLoadingMessages = function(state, newState) {\n if (!state.loadingMessages && newState.loadingMessages) {\n return true;\n } else if (state.loadingMessages && !newState.loadingMessages) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Determine if we should show the emoji picker.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|N
ull}\n */\n var buildShowEmojiPicker = function(state, newState) {\n if (!state.showEmojiPicker && newState.showEmojiPicker) {\n return true;\n } else if (state.showEmojiPicker && !newState.showEmojiPicker) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Determine if we should show the emoji auto complete.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildShowEmojiAutoComplete = function(state, newState) {\n if (!state.showEmojiAutoComplete && newState.showEmojiAutoComplete) {\n return true;\n } else if (state.showEmojiAutoComplete && !newState.showEmojiAutoComplete) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Get the user Object of user to be blocked if pending.\n *\n * @param {Object} state The current state
.\n * @param {Object} newState The new state.\n * @return {Object|Bool|Null} User Object if Object.\n */\n var buildConfirmBlockUser = function(state, newState) {\n if (newState.pendingBlockUserIds.length) {\n // We currently only support a single user;\n var userId = newState.pendingBlockUserIds[0];\n return newState.members[userId];\n } else if (state.pendingBlockUserIds.length) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Get the user Object of user to be unblocked if pending.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object|Bool|Null} User Object if Object.\n */\n var buildConfirmUnblockUser = function(state, newState) {\n if (newState.pendingUnblockUserIds.length) {\n // We currently only support a single user;\n var userId = newState.pendingUnblockUserIds[0];\n return
newState.members[userId];\n } else if (state.pendingUnblockUserIds.length) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Get the user Object of user to be added as contact if pending.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object|Bool|Null} User Object if Object.\n */\n var buildConfirmAddContact = function(state, newState) {\n if (newState.pendingAddContactIds.length) {\n // We currently only support a single user;\n var userId = newState.pendingAddContactIds[0];\n return newState.members[userId];\n } else if (state.pendingAddContactIds.length) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Get the user Object of user to be removed as contact if pending.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {
Object|Bool|Null} User Object if Object.\n */\n var buildConfirmRemoveContact = function(state, newState) {\n if (newState.pendingRemoveContactIds.length) {\n // We currently only support a single user;\n var userId = newState.pendingRemoveContactIds[0];\n return newState.members[userId];\n } else if (state.pendingRemoveContactIds.length) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Check if there are any messages to be deleted.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object|Null} The conversation type and if the user can delete the messages for all users.\n */\n var buildConfirmDeleteSelectedMessages = function(state, newState) {\n var oldPendingCount = state.pendingDeleteMessageIds.length;\n var newPendingCount = newState.pendingDeleteMessageIds.length;\n\n if (newPendingCount && !oldPendingCount)
{\n return {\n show: true,\n type: newState.type,\n canDeleteMessagesForAllUsers: newState.canDeleteMessagesForAllUsers\n };\n } else if (oldPendingCount && !newPendingCount) {\n return {\n show: false\n };\n }\n\n return null;\n };\n\n /**\n * Check if there is a conversation to be deleted.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {int|Null} The conversation type to be deleted.\n */\n var buildConfirmDeleteConversation = function(state, newState) {\n if (!state.pendingDeleteConversation && newState.pendingDeleteConversation) {\n return newState.type;\n } else if (state.pendingDeleteConversation && !newState.pendingDeleteConversation) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Check if there is a pending contact req
uest to accept or decline.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildConfirmContactRequest = function(state, newState) {\n var loggedInUserId = state.loggedInUserId;\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var oldReceivedRequests = !oldOtherUser ? [] : oldOtherUser.contactrequests.filter(function(request) {\n return request.requesteduserid == loggedInUserId && request.userid == oldOtherUser.id;\n });\n var newReceivedRequests = !newOtherUser ? [] : newOtherUser.contactrequests.filter(function(request) {\n return request.requesteduserid == loggedInUserId && request.userid == newOtherUser.id;\n });\n var oldRequest = oldReceivedRequests.length ? oldReceivedRequests[0] : null;\n var newRequest = newReceivedRequests.length ? newReceivedRequests[0] :
null;\n\n if (!oldRequest && newRequest) {\n return newOtherUser;\n } else if (oldRequest && !newRequest) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Check if there are any changes in blocked users.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildIsBlocked = function(state, newState) {\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n\n if (!oldOtherUser && !newOtherUser) {\n return null;\n } else if (!oldOtherUser && newOtherUser) {\n return newOtherUser.isblocked ? true : null;\n } else if (!newOtherUser && oldOtherUser) {\n return oldOtherUser.isblocked ? false : null;\n } else if (oldOtherUser.isblocked && !newOtherUser.isblocked) {\n return false;\n } else
if (!oldOtherUser.isblocked && newOtherUser.isblocked) {\n return true;\n } else {\n return null;\n }\n };\n\n /**\n * Check if there are any changes the conversation favourite state.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildIsFavourite = function(state, newState) {\n var oldIsFavourite = state.isFavourite;\n var newIsFavourite = newState.isFavourite;\n\n if (state.id === null && newState.id === null) {\n // The conversation isn't yet created so don't change anything.\n return null;\n } else if (state.id === null && newState.id !== null) {\n // The conversation was created so we can show the add favourite button.\n return 'show-add';\n } else if (state.id !== null && newState.id === null) {\n // We're changing from a created conversation to a new conversation
so hide\n // the favouriting functionality for now.\n return 'hide';\n } else if (oldIsFavourite == newIsFavourite) {\n // No change.\n return null;\n } else if (!oldIsFavourite && newIsFavourite) {\n return 'show-remove';\n } else if (oldIsFavourite && !newIsFavourite) {\n return 'show-add';\n } else {\n return null;\n }\n };\n\n /**\n * Check if there are any changes the conversation muted state.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {string|null}\n */\n var buildIsMuted = function(state, newState) {\n var oldIsMuted = state.isMuted;\n var newIsMuted = newState.isMuted;\n\n if (state.id === null && newState.id === null) {\n // The conversation isn't yet created so don't change anything.\n return null;\n } else if (state.id === null && newState.id
!== null) {\n // The conversation was created so we can show the mute button.\n return 'show-mute';\n } else if (state.id !== null && newState.id === null) {\n // We're changing from a created conversation to a new conversation so hide\n // the muting functionality for now.\n return 'hide';\n } else if (oldIsMuted == newIsMuted) {\n // No change.\n return null;\n } else if (!oldIsMuted && newIsMuted) {\n return 'show-unmute';\n } else if (oldIsMuted && !newIsMuted) {\n return 'show-mute';\n } else {\n return null;\n }\n };\n\n /**\n * Check if there are any changes in the contact status of the current user\n * and other user.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildIsContact = function(state, newState) {\n var loggedInUser
Id = state.loggedInUserId;\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var oldContactRequests = !oldOtherUser ? [] : oldOtherUser.contactrequests.filter(function(request) {\n return (request.userid == loggedInUserId && request.requesteduserid == oldOtherUser.id) ||\n (request.userid == oldOtherUser.id && request.requesteduserid == loggedInUserId);\n });\n var newContactRequests = !newOtherUser ? [] : newOtherUser.contactrequests.filter(function(request) {\n return (request.userid == loggedInUserId && request.requesteduserid == newOtherUser.id) ||\n (request.userid == newOtherUser.id && request.requesteduserid == loggedInUserId);\n });\n var oldHasContactRequests = oldContactRequests.length > 0;\n var newHasContactRequests = newContactRequests.length > 0;\n\n if (!oldOtherUser && !newOtherUser) {\n return null;\n } e
lse if (oldHasContactRequests && newHasContactRequests) {\n return null;\n } else if (!oldHasContactRequests && newHasContactRequests && !newOtherUser.iscontact) {\n return 'pending-contact';\n } else if (!oldOtherUser && newOtherUser) {\n return newOtherUser.iscontact ? 'contact' : null;\n } else if (!newOtherUser && oldOtherUser) {\n return oldOtherUser.iscontact ? 'non-contact' : null;\n } else if (oldOtherUser.iscontact && !newOtherUser.iscontact) {\n return newHasContactRequests ? 'pending-contact' : 'non-contact';\n } else if (!oldOtherUser.iscontact && newOtherUser.iscontact) {\n return 'contact';\n } else {\n return null;\n }\n };\n\n /**\n * Check if a confirm action is active.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildLoadingConfirmationAction = funct
ion(state, newState) {\n if (!state.loadingConfirmAction && newState.loadingConfirmAction) {\n return true;\n } else if (state.loadingConfirmAction && !newState.loadingConfirmAction) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Check if a edit mode is active.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildInEditMode = function(state, newState) {\n var oldHasSelectedMessages = state.selectedMessageIds.length > 0;\n var newHasSelectedMessages = newState.selectedMessageIds.length > 0;\n var numberOfMessagesHasChanged = state.messages.length != newState.messages.length;\n\n if (!oldHasSelectedMessages && newHasSelectedMessages) {\n return true;\n } else if (oldHasSelectedMessages && !newHasSelectedMessages) {\n return false;\n } else if (oldHasSelecte
dMessages && numberOfMessagesHasChanged) {\n return true;\n } else {\n return null;\n }\n };\n\n /**\n * Build a patch for the messages selected.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} patch\n */\n var buildSelectedMessages = function(state, newState) {\n var oldSelectedMessages = state.selectedMessageIds;\n var newSelectedMessages = newState.selectedMessageIds;\n\n if (isArrayEqual(oldSelectedMessages, newSelectedMessages)) {\n return null;\n }\n\n var diff = diffArrays(oldSelectedMessages, newSelectedMessages, function(a, b) {\n return a == b;\n });\n\n return {\n count: newSelectedMessages.length,\n add: diff.missingFromA,\n remove: diff.missingFromB\n };\n };\n\n /**\n * Get a list of users from the state that are not the logged in user. Use
to find group\n * message members or the other user in a conversation.\n *\n * @param {Object} state State\n * @return {Array} List of users.\n */\n var getOtherUserFromState = function(state) {\n return Object.keys(state.members).reduce(function(carry, userId) {\n if (userId != state.loggedInUserId && !carry) {\n carry = state.members[userId];\n }\n\n return carry;\n }, null);\n };\n\n /**\n * Check if the given user requires a contact request from the logged in user.\n *\n * @param {Integer} loggedInUserId The logged in user id\n * @param {Object} user User record\n * @return {Bool}\n */\n var requiresContactRequest = function(loggedInUserId, user) {\n // If a user can message then no contact request is required.\n if (user.canmessage) {\n return false;\n }\n\n var contactRequests = user.contactrequests.filter(function(request) {\n return
request.userid == loggedInUserId || request.requesteduserid;\n });\n var hasSentContactRequest = contactRequests.length > 0;\n return user.requirescontact && !user.iscontact && !hasSentContactRequest;\n };\n\n /**\n * Check if other users are required to be added as contact.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} Object controlling the required to add contact dialog variables.\n */\n var buildRequireAddContact = function(state, newState) {\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var hadMessages = state.messages.length > 0;\n var hasMessages = newState.messages.length > 0;\n var loggedInUserId = newState.loggedInUserId;\n var prevRequiresContactRequest = oldOtherUser && requiresContactRequest(loggedInUserId, oldOtherUser);\n var nextRequiresContactRequest = newOther
User && requiresContactRequest(loggedInUserId, newOtherUser);\n var confirmAddContact = buildConfirmAddContact(state, newState);\n var finishedAddContact = confirmAddContact === false;\n\n // Still doing first load.\n if (!state.hasTriedToLoadMessages && !newState.hasTriedToLoadMessages) {\n return null;\n }\n\n // No users yet.\n if (!oldOtherUser && !newOtherUser) {\n return null;\n }\n\n // We've loaded a new user and they require a contact request.\n if (!oldOtherUser && nextRequiresContactRequest) {\n return {\n show: true,\n hasMessages: hasMessages,\n user: newOtherUser\n };\n }\n\n // The logged in user has completed the confirm contact request dialogue\n // but the other user still requires a contact request which means the logged\n // in user either declined the confirmation or it failed.\n if (finishedAddCo
ntact && nextRequiresContactRequest) {\n return {\n show: true,\n hasMessages: hasMessages,\n user: newOtherUser\n };\n }\n\n // Everything is loaded.\n if (state.hasTriedToLoadMessages && newState.hasTriedToLoadMessages) {\n if (!prevRequiresContactRequest && nextRequiresContactRequest) {\n return {\n show: true,\n hasMessages: hasMessages,\n user: newOtherUser\n };\n }\n\n if (prevRequiresContactRequest && !nextRequiresContactRequest) {\n return {\n show: false,\n hasMessages: hasMessages\n };\n }\n }\n\n // First load just completed.\n if (!state.hasTriedToLoadMessages && newState.hasTriedToLoadMessages) {\n if (nextRequiresContactRequest) {\n return {\n show
: true,\n hasMessages: hasMessages,\n user: newOtherUser\n };\n }\n }\n\n // Being reset.\n if (state.hasTriedToLoadMessages && !newState.hasTriedToLoadMessages) {\n if (prevRequiresContactRequest) {\n return {\n show: false,\n hasMessages: hadMessages\n };\n }\n }\n\n return null;\n };\n\n /**\n * Check if other users are required to be unblocked.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildRequireUnblock = function(state, newState) {\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n\n if (!oldOtherUser && !newOtherUser) {\n return null;\n } else if (oldOtherUser && !newOtherUser) {\n return old
OtherUser.isblocked ? false : null;\n } else if (!oldOtherUser && newOtherUser) {\n return newOtherUser.isblocked ? true : null;\n } else if (!oldOtherUser.isblocked && newOtherUser.isblocked) {\n return true;\n } else if (oldOtherUser.isblocked && !newOtherUser.isblocked) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Check if other users can be messaged.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildUnableToMessage = function(state, newState) {\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n\n if (newState.type == Constants.CONVERSATION_TYPES.SELF) {\n // Users always can send message themselves on self-conversations.\n return null;\n }\n\n if (!oldOtherUser && !newOtherUser) {\n return
null;\n } else if (oldOtherUser && !newOtherUser) {\n return oldOtherUser.canmessage ? null : true;\n } else if (!oldOtherUser && newOtherUser) {\n return newOtherUser.canmessage ? null : true;\n } else if (!oldOtherUser.canmessage && newOtherUser.canmessage) {\n return false;\n } else if (oldOtherUser.canmessage && !newOtherUser.canmessage) {\n return true;\n }\n\n return null;\n };\n\n /**\n * Build patch for footer information for a private conversation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} containing footer state type.\n */\n var buildFooterPatchTypePrivate = function(state, newState) {\n var loadingFirstMessages = buildLoadingFirstMessages(state, newState);\n var inEditMode = buildInEditMode(state, newState);\n var requireAddContact = buildRequireAddContact(state, newState);\n var
requireUnblock = buildRequireUnblock(state, newState);\n var unableToMessage = buildUnableToMessage(state, newState);\n var showRequireAddContact = requireAddContact !== null ? requireAddContact.show && requireAddContact.hasMessages : null;\n var otherUser = getOtherUserFromState(newState);\n var generateReturnValue = function(checkValue, successReturn) {\n if (checkValue) {\n return successReturn;\n } else if (checkValue !== null && !checkValue) {\n if (!otherUser) {\n return {type: 'content'};\n } else if (otherUser.isblocked) {\n return {type: 'unblock'};\n } else if (newState.messages.length && requiresContactRequest(newState.loggedInUserId, otherUser)) {\n return {\n type: 'add-contact',\n user: otherUser\n };\n } else if (!otherUser.canmessage && (otherUser.r
equirescontact && !otherUser.iscontact)) {\n return {type: 'unable-to-message'};\n }\n }\n\n return null;\n };\n\n if (\n loadingFirstMessages === null &&\n inEditMode === null &&\n requireAddContact === null &&\n requireUnblock === null\n ) {\n return null;\n }\n\n var checks = [\n [loadingFirstMessages, {type: 'placeholder'}],\n [inEditMode, {type: 'edit-mode'}],\n [unableToMessage, {type: 'unable-to-message'}],\n [requireUnblock, {type: 'unblock'}],\n [showRequireAddContact, {type: 'add-contact', user: otherUser}]\n ];\n\n for (var i = 0; i < checks.length; i++) {\n var checkValue = checks[i][0];\n var successReturn = checks[i][1];\n var result = generateReturnValue(checkValue, successReturn);\n\n if (result !== null) {\n return result;
\n }\n }\n\n return {\n type: 'content'\n };\n };\n\n /**\n * Build patch for footer information for a public conversation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} containing footer state type.\n */\n var buildFooterPatchTypePublic = function(state, newState) {\n var loadingFirstMessages = buildLoadingFirstMessages(state, newState);\n var inEditMode = buildInEditMode(state, newState);\n\n if (loadingFirstMessages === null && inEditMode === null) {\n return null;\n }\n\n if (loadingFirstMessages) {\n return {type: 'placeholder'};\n }\n\n if (inEditMode) {\n return {type: 'edit-mode'};\n }\n\n return {\n type: 'content'\n };\n };\n\n /**\n * Check if we're viewing a different conversation. If so then we need to\n * reset the UI.\n
*\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {bool|null} If a reset needs to occur\n */\n var buildReset = function(state, newState) {\n var oldType = state.type;\n var newType = newState.type;\n var oldConversationId = state.id;\n var newConversationId = newState.id;\n var oldMemberIds = Object.keys(state.members);\n var newMemberIds = Object.keys(newState.members);\n\n oldMemberIds.sort();\n newMemberIds.sort();\n\n var membersUnchanged = oldMemberIds.every(function(id, index) {\n return id == newMemberIds[index];\n });\n\n if (oldType != newType) {\n // If we've changed conversation type then we need to reset.\n return true;\n } else if (oldConversationId && !newConversationId) {\n // We previously had a conversation id but no longer do. This likely means\n // the user is viewing the convers
ation with someone they've never spoken to\n // before.\n return true;\n } else if (oldConversationId && newConversationId && oldConversationId != newConversationId) {\n // If we had a conversation id and it's changed then we need to reset.\n return true;\n } else if (!oldConversationId && !newConversationId && !membersUnchanged) {\n // If we never had a conversation id but the members of the conversation have\n // changed then we need to reset. This can happen if the user goes from viewing\n // a user they've never had a conversation with to viewing a different user that\n // they've never had a conversation with.\n return true;\n }\n\n return null;\n };\n\n /**\n * We should show this message always, for all the self-conversations.\n *\n * The message should be hidden when it's not a self-conversation.\n *\n * @param {Object} state The current state.\n
* @param {Object} newState The new state.\n * @return {bool}\n */\n var buildSelfConversationMessage = function(state, newState) {\n if (state.type != newState.type) {\n return (newState.type == Constants.CONVERSATION_TYPES.SELF);\n }\n\n return null;\n };\n\n /**\n * We should show the contact request sent message if the user just sent\n * a contact request to the other user\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {string|false|null}\n */\n var buildContactRequestSent = function(state, newState) {\n var loggedInUserId = newState.loggedInUserId;\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var oldSentRequests = !oldOtherUser ? [] : oldOtherUser.contactrequests.filter(function(request) {\n return request.userid == loggedInUserId;\n });\n var newSent
Requests = !newOtherUser ? [] : newOtherUser.contactrequests.filter(function(request) {\n return request.userid == loggedInUserId;\n });\n var oldRequest = oldSentRequests.length > 0;\n var newRequest = newSentRequests.length > 0;\n\n if (!oldRequest && newRequest && !newOtherUser.iscontact) {\n return newOtherUser.fullname;\n } else if (oldOtherUser && !oldOtherUser.iscontact && newRequest && newOtherUser.iscontact) {\n // Contact request accepted.\n return false;\n } else if (oldRequest && !newRequest) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Build the full patch comparing the current state and the new state. This patch is used by\n * the conversation renderer to render the UI on any update.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} Patch containing all informa
tion changed.\n */\n var buildPatch = function(state, newState) {\n var config = {\n all: {\n reset: buildReset,\n conversation: buildConversationPatch,\n scrollToMessage: buildScrollToMessagePatch,\n loadingMembers: buildLoadingMembersPatch,\n loadingFirstMessages: buildLoadingFirstMessages,\n loadingMessages: buildLoadingMessages,\n confirmDeleteSelectedMessages: buildConfirmDeleteSelectedMessages,\n inEditMode: buildInEditMode,\n selectedMessages: buildSelectedMessages,\n isFavourite: buildIsFavourite,\n isMuted: buildIsMuted,\n showEmojiPicker: buildShowEmojiPicker,\n showEmojiAutoComplete: buildShowEmojiAutoComplete\n }\n };\n // These build functions are only applicable to private conversations.\n config[Constants.CONVERSATION_TYPES.PRIVATE] = {\n
header: buildHeaderPatchTypePrivate,\n footer: buildFooterPatchTypePrivate,\n confirmBlockUser: buildConfirmBlockUser,\n confirmUnblockUser: buildConfirmUnblockUser,\n confirmAddContact: buildConfirmAddContact,\n confirmRemoveContact: buildConfirmRemoveContact,\n confirmContactRequest: buildConfirmContactRequest,\n confirmDeleteConversation: buildConfirmDeleteConversation,\n isBlocked: buildIsBlocked,\n isContact: buildIsContact,\n loadingConfirmAction: buildLoadingConfirmationAction,\n requireAddContact: buildRequireAddContact,\n contactRequestSent: buildContactRequestSent\n };\n // These build functions are only applicable to public (group) conversations.\n config[Constants.CONVERSATION_TYPES.PUBLIC] = {\n header: buildHeaderPatchTypePublic,\n footer: buildFooterPatchTypePublic,\n };\n // These build functions are on
ly applicable to self-conversations.\n config[Constants.CONVERSATION_TYPES.SELF] = {\n header: buildHeaderPatchTypeSelf,\n footer: buildFooterPatchTypePublic,\n confirmDeleteConversation: buildConfirmDeleteConversation,\n selfConversationMessage: buildSelfConversationMessage\n };\n\n var patchConfig = $.extend({}, config.all);\n if (newState.type && newState.type in config) {\n // Add the type specific builders to the patch config.\n patchConfig = $.extend(patchConfig, config[newState.type]);\n }\n\n return Object.keys(patchConfig).reduce(function(patch, key) {\n var buildFunc = patchConfig[key];\n var value = buildFunc(state, newState);\n\n if (value !== null) {\n patch[key] = value;\n }\n\n return patch;\n }, {});\n };\n\n return {\n buildPatch: buildPatch\n };\n});\n"],"names":["define","$","UserDate","Const
ants","sortMessagesByDay","messages","midnight","messagesByDay","reduce","carry","message","timeCreated","dayTimestamp","getUserMidnightForTimestamp","hasOwnProperty","push","Object","keys","map","timestamp","diffArrays","a","b","matchFunction","slice","missingFromB","matches","forEach","current","found","index","length","next","splice","missingFromA","findPositionInArray","array","breakFunction","i","candidate","isArrayEqual","sort","aLength","bLength","every","item","isObjectEqual","aKeys","bKeys","key","aVal","bVal","aType","bType","Array","isArray","isMessageEqual","id","state","sendState","text","buildDaysPatch","remove","add","day","before","value","buildConversationPatch","newState","matchingDays","update","diff","daysDiff","dayCurrent","dayNext","days","messagesDiff","patch","timeAdded","concat","after","buildHeaderPatchTypePrivate","requireAddContact","buildRequireAddContact","confirmContactRequest","buildConfirmContactRequest","oldOtherUser","getOtherUserFromState","newOtherUser","requiresAddContac
t","show","hasMessages","requiredAddContact","shouldRenderHeader","type","CONVERSATION_TYPES","PRIVATE","showControls","context","name","subname","totalmembercount","totalMemberCount","imageurl","imageUrl","isfavourite","isFavourite","ismuted","isMuted","showfavourite","userid","showonlinestatus","isonline","isblocked","iscontact","buildHeaderPatchTypeSelf","SELF","buildHeaderPatchTypePublic","PUBLIC","buildScrollToMessagePatch","oldMessages","newMessages","previousNewest","currentNewest","previousOldest","currentOldest","buildLoadingMembersPatch","loadingMembers","buildLoadingFirstMessages","hasTriedToLoadMessages","loadingMessages","buildLoadingMessages","buildShowEmojiPicker","showEmojiPicker","buildShowEmojiAutoComplete","showEmojiAutoComplete","buildConfirmBlockUser","pendingBlockUserIds","userId","members","buildConfirmUnblockUser","pendingUnblockUserIds","buildConfirmAddContact","pendingAddContactIds","buildConfirmRemoveContact","pendingRemoveContactIds","buildConfirmDeleteSelectedMessages","oldPendin
gCount","pendingDeleteMessageIds","newPendingCount","canDeleteMessagesForAllUsers","buildConfirmDeleteConversation","pendingDeleteConversation","loggedInUserId","oldReceivedRequests","contactrequests","filter","request","requesteduserid","newReceivedRequests","oldRequest","newRequest","buildIsBlocked","buildIsFavourite","oldIsFavourite","newIsFavourite","buildIsMuted","oldIsMuted","newIsMuted","buildIsContact","oldContactRequests","newContactRequests","oldHasContactRequests","newHasContactRequests","buildLoadingConfirmationAction","loadingConfirmAction","buildInEditMode","oldHasSelectedMessages","selectedMessageIds","newHasSelectedMessages","numberOfMessagesHasChanged","buildSelectedMessages","oldSelectedMessages","newSelectedMessages","count","requiresContactRequest","user","canmessage","hasSentContactRequest","requirescontact","hadMessages","prevRequiresContactRequest","nextRequiresContactRequest","finishedAddContact","buildFooterPatchTypePrivate","loadingFirstMessages","inEditMode","requireUnblock","build
RequireUnblock","unableToMessage","buildUnableToMessage","showRequireAddContact","otherUser","generateReturnValue","checkValue","successReturn","checks","result","buildFooterPatchTypePublic","buildReset","oldType","newType","oldConversationId","newConversationId","oldMemberIds","newMemberIds","membersUnchanged","buildSelfConversationMessage","buildContactRequestSent","oldSentRequests","newSentRequests","fullname","buildPatch","config","all","reset","conversation","scrollToMessage","confirmDeleteSelectedMessages","selectedMessages","header","footer","confirmBlockUser","confirmUnblockUser","confirmAddContact","confirmRemoveContact","confirmDeleteConversation","isBlocked","isContact","contactRequestSent","selfConversationMessage","patchConfig","extend","buildFunc"],"mappings":"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"}