diff --git a/public/lang/en/deprecated.txt b/public/lang/en/deprecated.txt index 2d59acd9918..4704775b99c 100644 --- a/public/lang/en/deprecated.txt +++ b/public/lang/en/deprecated.txt @@ -86,3 +86,4 @@ maxsectionslimit,core maxsectionaddmessage,core_courseformat activities_help,core resources_help,core +unabletomessage,core_message diff --git a/public/lang/en/message.php b/public/lang/en/message.php index a73bcfb169e..93c4bfb9fd4 100644 --- a/public/lang/en/message.php +++ b/public/lang/en/message.php @@ -39,6 +39,7 @@ $string['blockuserconfirm'] = 'Are you sure you want to block {$a}?'; $string['blockuserconfirmbutton'] = 'Block'; $string['blocknoncontacts'] = 'Prevent non-contacts from messaging me'; $string['cancelselection'] = 'Cancel message selection'; +$string['cannotsendmessages'] = 'Cannot send messages'; $string['cantblockuser'] = 'You can\'t block {$a} because they have a role with permission to message all users.'; $string['contactableprivacy'] = 'Accept messages from:'; $string['contactableprivacy_onlycontacts'] = 'My contacts only'; @@ -128,6 +129,7 @@ $string['nocontactsgetstarted'] = 'No contacts'; $string['nogroupconversations'] = 'No group conversations'; $string['noindividualconversations'] = 'No private conversations'; $string['nomessagesfound'] = 'No messages were found'; +$string['nopermissiontosend'] = 'You do not have permission to send messages in this conversation'; $string['noreply'] = 'Do not reply to this message'; $string['noncontacts'] = 'Non-contacts'; $string['nonotifications'] = 'You have no notifications'; @@ -253,7 +255,6 @@ $string['successproviderupdate'] = '\'{$a}\' updated.'; $string['togglemessagemenu'] = 'Toggle messaging drawer'; $string['totalconversations'] = '{$a} total conversations'; $string['touserdoesntexist'] = 'You cannot send a message to a user ID ({$a}) that doesn\'t exist.'; -$string['unabletomessage'] = 'You are unable to message this user'; $string['unblock'] = 'Unblock'; $string['unblockcontact'] = 'Unblock contact'; $string['unblockuser'] = 'Unblock user'; @@ -283,3 +284,6 @@ $string['yourcontactrequestpending'] = 'Your contact request is pending with {$a // Deprecated since Moodle 5.0. $string['togglenotificationmenu'] = 'Toggle notifications menu'; + +// Deprecated since Moodle 5.1. +$string['unabletomessage'] = 'You are unable to message this user'; diff --git a/public/message/amd/build/message_drawer_view_conversation.min.js b/public/message/amd/build/message_drawer_view_conversation.min.js index 62f140b7996..097888be553 100644 --- a/public/message/amd/build/message_drawer_view_conversation.min.js +++ b/public/message/amd/build/message_drawer_view_conversation.min.js @@ -37,6 +37,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("core_message/message_drawer_view_conversation",["jquery","core/auto_rows","core/backoff_timer","core/custom_interaction_events","core/notification","core/pending","core/pubsub","core/str","core_message/message_repository","core_message/message_drawer_events","core_message/message_drawer_view_conversation_constants","core_message/message_drawer_view_conversation_patcher","core_message/message_drawer_view_conversation_renderer","core_message/message_drawer_view_conversation_state_manager","core_message/message_drawer_router","core_message/message_drawer_routes","core/emoji/auto_complete","core/emoji/picker"],(function($,AutoRows,BackOffTimer,CustomEvents,Notification,Pending,PubSub,Str,Repository,MessageDrawerEvents,Constants,Patcher,Renderer,StateManager,MessageDrawerRouter,MessageDrawerRoutes,initialiseEmojiAutoComplete,initialiseEmojiPicker){var stateCache={},viewState=null,loadedAllMessages=!1,messagesOffset=0,newMessagesPollTimer=null,isRendering=!1,renderBuffer=[],isResetting=!0,isSendingMessage=!1,isDeletingConversationContent=!1,sendMessageBuffer=[],render=null,renderers=[],NEWEST_FIRST=Constants.NEWEST_MESSAGES_FIRST,LOAD_MESSAGE_LIMIT=Constants.LOAD_MESSAGE_LIMIT,MILLISECONDS_IN_SEC=Constants.MILLISECONDS_IN_SEC,SELECTORS=Constants.SELECTORS,CONVERSATION_TYPES=Constants.CONVERSATION_TYPES,getOtherUserId=function(){if(!viewState||viewState.type==CONVERSATION_TYPES.PUBLIC)return null;var loggedInUserId=viewState.loggedInUserId;if(viewState.type==CONVERSATION_TYPES.SELF)return loggedInUserId;var otherUserIds=Object.keys(viewState.members).filter((function(userId){return loggedInUserId!=userId}));return otherUserIds.length?otherUserIds[0]:null},getMessagesOffset=function(){return messagesOffset},setMessagesOffset=function(value){messagesOffset=value,stateCache[viewState.id].messagesOffset=value},hasLoadedAllMessages=function(){return loadedAllMessages},setLoadedAllMessages=function(value){loadedAllMessages=value,stateCache[viewState.id].loadedAllMessages=value},formatConversationForEvent=function(state){return{id:state.id,name:state.name,subname:state.subname,imageUrl:state.imageUrl,isFavourite:state.isFavourite,isMuted:state.isMuted,type:state.type,totalMemberCount:state.totalMemberCount,loggedInUserId:state.loggedInUserId,messages:state.messages.map((function(message){return $.extend({},message)})),members:Object.keys(state.members).map((function(id){var formattedMember=$.extend({},state.members[id]);return formattedMember.contactrequests=state.members[id].contactrequests.map((function(request){return $.extend({},request)})),formattedMember}))}},updateStateFromConversation=function(conversation,loggedInUserId){var otherUser=null;if(conversation.type==CONVERSATION_TYPES.PRIVATE){var otherUsers=conversation.members.filter((function(member){return member.id!=loggedInUserId}));otherUser=otherUsers.length?otherUsers[0]:null}else conversation.type==CONVERSATION_TYPES.SELF&&(otherUser=conversation.members[0]);var name=conversation.name,imageUrl=conversation.imageurl;conversation.type!=CONVERSATION_TYPES.PUBLIC&&(name=name||otherUser?otherUser.fullname:"",imageUrl=imageUrl||otherUser?otherUser.profileimageurl:"");var newState=StateManager.addMembers(viewState,conversation.members);return newState=StateManager.setName(newState,name),newState=StateManager.setSubname(newState,conversation.subname),newState=StateManager.setType(newState,conversation.type),newState=StateManager.setImageUrl(newState,imageUrl),newState=StateManager.setTotalMemberCount(newState,conversation.membercount),newState=StateManager.setIsFavourite(newState,conversation.isfavourite),newState=StateManager.setIsMuted(newState,conversation.ismuted),newState=StateManager.addMessages(newState,conversation.messages),newState=StateManager.setCanDeleteMessagesForAllUsers(newState,conversation.candeletemessagesforallusers)},loadMessages=function(conversationId,limit,offset,newestFirst,ignoreList,timeFrom){return Repository.getMessages(viewState.loggedInUserId,conversationId,limit?limit+1:limit,offset,newestFirst,timeFrom).then((function(result){return result.id!=viewState.id&&(result.messages=[],result.id in stateCache&&delete stateCache[result.id]),result})).then((function(result){return result.messages.length&&ignoreList.length&&(result.messages=result.messages.filter((function(message){return ignoreList.indexOf(parseInt(message.id,10))<0}))),result})).then((function(result){return limit?(result.messages.length>limit?result.messages=result.messages.slice(0,-1):setLoadedAllMessages(!0),result):result})).then((function(result){var membersToAdd=result.members.filter((function(member){return!(member.id in viewState.members)})),newState=StateManager.addMembers(viewState,membersToAdd);return newState=StateManager.addMessages(newState,result.messages),newState=StateManager.setLoadingMessages(newState,!1),render(newState).then((function(){return result}))})).catch((function(error){var newState=StateManager.setLoadingMessages(viewState,!1);throw render(newState),error}))},markConversationAsRead=function(conversationId){var loggedInUserId=viewState.loggedInUserId,pendingPromise=new Pending("core_message/message_drawer_view_conversation:markConversationAsRead");return Repository.markAllConversationMessagesAsRead(loggedInUserId,conversationId).then((function(){var newState=StateManager.markMessagesAsRead(viewState,viewState.messages);return PubSub.publish(MessageDrawerEvents.CONVERSATION_READ,conversationId),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestBlockUser=function(userId){cancelRequest(userId);var newState=StateManager.addPendingBlockUsersById(viewState,[userId]);render(newState)},blockUser=function(userId){var newState=StateManager.setLoadingConfirmAction(viewState,!0),pendingPromise=new Pending("core_message/message_drawer_view_conversation:blockUser");return render(newState),Repository.blockUser(viewState.loggedInUserId,userId).then((function(profile){var newState=StateManager.addMembers(viewState,[profile]);return newState=StateManager.removePendingBlockUsersById(newState,[userId]),newState=StateManager.setLoadingConfirmAction(newState,!1),PubSub.publish(MessageDrawerEvents.CONTACT_BLOCKED,userId),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestUnblockUser=function(userId){cancelRequest(userId);var newState=StateManager.addPendingUnblockUsersById(viewState,[userId]);render(newState)},unblockUser=function(userId){var newState=StateManager.setLoadingConfirmAction(viewState,!0),pendingPromise=new Pending("core_message/message_drawer_view_conversation:unblockUser");return render(newState),Repository.unblockUser(viewState.loggedInUserId,userId).then((function(profile){var newState=StateManager.addMembers(viewState,[profile]);return newState=StateManager.removePendingUnblockUsersById(newState,[userId]),newState=StateManager.setLoadingConfirmAction(newState,!1),PubSub.publish(MessageDrawerEvents.CONTACT_UNBLOCKED,userId),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestRemoveContact=function(userId){cancelRequest(userId);var newState=StateManager.addPendingRemoveContactsById(viewState,[userId]);render(newState)},removeContact=function(userId){var newState=StateManager.setLoadingConfirmAction(viewState,!0),pendingPromise=new Pending("core_message/message_drawer_view_conversation:removeContact");return render(newState),Repository.deleteContacts(viewState.loggedInUserId,[userId]).then((function(profiles){var newState=StateManager.addMembers(viewState,profiles);return newState=StateManager.removePendingRemoveContactsById(newState,[userId]),newState=StateManager.setLoadingConfirmAction(newState,!1),PubSub.publish(MessageDrawerEvents.CONTACT_REMOVED,userId),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestAddContact=function(userId){cancelRequest(userId);var newState=StateManager.addPendingAddContactsById(viewState,[userId]);render(newState)},addContact=function(userId){var newState=StateManager.setLoadingConfirmAction(viewState,!0),pendingPromise=new Pending("core_message/message_drawer_view_conversation:addContactRequests");return render(newState),Repository.createContactRequest(viewState.loggedInUserId,userId).then((function(response){if(!response.request)throw new Error(response.warnings[0].message);return response.request})).then((function(request){var newState=StateManager.removePendingAddContactsById(viewState,[userId]);return newState=StateManager.addContactRequests(newState,[request]),newState=StateManager.setLoadingConfirmAction(newState,!1),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestDeleteSelectedMessages=function(userId){var selectedMessageIds=viewState.selectedMessageIds;cancelRequest(userId);var newState=StateManager.addPendingDeleteMessagesById(viewState,selectedMessageIds);render(newState)},deleteSelectedMessages=function(){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:deleteSelectedMessages"),messageIds=viewState.pendingDeleteMessageIds,sentMessages=viewState.messages.filter((function(message){return messageIds.indexOf(message.id)>=0&&("sent"==message.sendState||null===message.sendState)})),newState=StateManager.setLoadingConfirmAction(viewState,!0);render(newState);var deleteMessagesPromise=$.Deferred().resolve().promise();if(sentMessages.length){var sentMessageIds=sentMessages.map((function(message){return message.id}));deleteMessagesPromise=newState.deleteMessagesForAllUsers?Repository.deleteMessagesForAllUsers(viewState.loggedInUserId,sentMessageIds):Repository.deleteMessages(viewState.loggedInUserId,sentMessageIds)}return isDeletingConversationContent=!0,newMessagesPollTimer&&newMessagesPollTimer.stop(),deleteMessagesPromise.then((function(){var newState=StateManager.removeMessagesById(viewState,messageIds);newState=StateManager.removePendingDeleteMessagesById(newState,messageIds),newState=StateManager.removeSelectedMessagesById(newState,messageIds),newState=StateManager.setLoadingConfirmAction(newState,!1),newState=StateManager.setDeleteMessagesForAllUsers(newState,!1);var prevLastMessage=viewState.messages[viewState.messages.length-1],newLastMessage=newState.messages.length?newState.messages[newState.messages.length-1]:null;if(newLastMessage&&newLastMessage.id!=prevLastMessage.id){var conversation=formatConversationForEvent(newState);PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE,conversation)}else newState.messages.length||PubSub.publish(MessageDrawerEvents.CONVERSATION_DELETED,newState.id);return isDeletingConversationContent=!1,render(newState)})).then((function(result){return pendingPromise.resolve(),result})).catch(Notification.exception)},requestDeleteConversation=function(userId){cancelRequest(userId);var newState=StateManager.setPendingDeleteConversation(viewState,!0);render(newState)},deleteConversation=function(){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:markConversationAsRead"),newState=StateManager.setLoadingConfirmAction(viewState,!0);return render(newState),isDeletingConversationContent=!0,newMessagesPollTimer&&newMessagesPollTimer.stop(),Repository.deleteConversation(viewState.loggedInUserId,viewState.id).then((function(){var newState=StateManager.removeMessages(viewState,viewState.messages);return newState=StateManager.removeSelectedMessagesById(newState,viewState.selectedMessageIds),newState=StateManager.setPendingDeleteConversation(newState,!1),newState=StateManager.setLoadingConfirmAction(newState,!1),PubSub.publish(MessageDrawerEvents.CONVERSATION_DELETED,newState.id),isDeletingConversationContent=!1,render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},cancelRequest=function(userId){var pendingDeleteMessageIds=viewState.pendingDeleteMessageIds,newState=StateManager.removePendingAddContactsById(viewState,[userId]);newState=StateManager.removePendingRemoveContactsById(newState,[userId]),newState=StateManager.removePendingUnblockUsersById(newState,[userId]),newState=StateManager.removePendingBlockUsersById(newState,[userId]),newState=StateManager.removePendingDeleteMessagesById(newState,pendingDeleteMessageIds),newState=StateManager.setPendingDeleteConversation(newState,!1),newState=StateManager.setDeleteMessagesForAllUsers(newState,!1),render(newState)},acceptContactRequest=function(userId){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:acceptContactRequest"),loggedInUserId=viewState.loggedInUserId,requests=viewState.members[userId].contactrequests.filter((function(request){return request.requesteduserid==loggedInUserId})),request=requests[0],newState=StateManager.setLoadingConfirmAction(viewState,!0);return render(newState),Repository.acceptContactRequest(userId,loggedInUserId).then((function(profile){var newState=StateManager.removeContactRequests(viewState,[request]);return newState=StateManager.addMembers(viewState,[profile]),newState=StateManager.setLoadingConfirmAction(newState,!1),render(newState)})).then((function(){PubSub.publish(MessageDrawerEvents.CONTACT_ADDED,viewState.members[userId]),PubSub.publish(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED,request)})).then((function(result){return pendingPromise.resolve(),result}))},declineContactRequest=function(userId){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:declineContactRequest"),loggedInUserId=viewState.loggedInUserId,requests=viewState.members[userId].contactrequests.filter((function(request){return request.requesteduserid==loggedInUserId})),request=requests[0],newState=StateManager.setLoadingConfirmAction(viewState,!0);return render(newState),Repository.declineContactRequest(userId,loggedInUserId).then((function(profile){var newState=StateManager.removeContactRequests(viewState,[request]);return newState=StateManager.addMembers(viewState,[profile]),newState=StateManager.setLoadingConfirmAction(newState,!1),render(newState)})).then((function(){PubSub.publish(MessageDrawerEvents.CONTACT_REQUEST_DECLINED,request)})).then((function(result){return pendingPromise.resolve(),result}))},processSendMessageBuffer=function(){if(!isSendingMessage&&sendMessageBuffer.length){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:processSendMessageBuffer");isSendingMessage=!0;var messagesToSend=sendMessageBuffer.slice();sendMessageBuffer=[];var conversationId=viewState.id,newConversationId=null,messagesText=messagesToSend.map((function(message){return message.text})),messageIds=messagesToSend.map((function(message){return message.id})),sendMessagePromise=null,newCanDeleteMessagesForAllUsers=null;if(conversationId||viewState.type==CONVERSATION_TYPES.PUBLIC)sendMessagePromise=Repository.sendMessagesToConversation(conversationId,messagesText);else{var otherUserId=getOtherUserId();sendMessagePromise=Repository.sendMessagesToUser(otherUserId,messagesText).then((function(messages){return messages.length&&(newConversationId=parseInt(messages[0].conversationid,10),newCanDeleteMessagesForAllUsers=messages[0].candeletemessagesforallusers),messages}))}sendMessagePromise.then((function(messages){var newMessageIds=messages.map((function(message){return message.id})),data=[],selectedToRemove=[],selectedToAdd=[];messagesToSend.forEach((function(oldMessage,index){var newMessage=messages[index];data.push([oldMessage,newMessage]),viewState.selectedMessageIds.indexOf(oldMessage.id)>=0&&(selectedToRemove.push(oldMessage.id),selectedToAdd.push(newMessage.id))}));var newState=StateManager.updateMessages(viewState,data);newState=StateManager.setMessagesSendSuccessById(newState,newMessageIds),selectedToRemove.length&&(newState=StateManager.removeSelectedMessagesById(newState,selectedToRemove)),selectedToAdd.length&&(newState=StateManager.addSelectedMessagesById(newState,selectedToAdd));var conversation=formatConversationForEvent(newState);newState.id||(newState=StateManager.setId(newState,newConversationId),conversation.id=newConversationId,resetMessagePollTimer(newConversationId),PubSub.publish(MessageDrawerEvents.CONVERSATION_CREATED,conversation),newState=StateManager.setCanDeleteMessagesForAllUsers(newState,newCanDeleteMessagesForAllUsers)),render(newState),isSendingMessage=!1,processSendMessageBuffer(),PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE,conversation)})).then((function(result){return pendingPromise.resolve(),result})).catch((function(e){var errorMessage;errorMessage=e.message?$.Deferred().resolve(e.message).promise():Str.get_string("unknownerror","core");var handleFailedMessages=function(errorMessage){var newState=StateManager.setMessagesSendFailById(viewState,messageIds,errorMessage);render(newState),isSendingMessage=!1,processSendMessageBuffer()};errorMessage.then(handleFailedMessages).then((function(result){return pendingPromise.resolve(),result})).catch((function(e){var finalError=e.message||"Something went wrong!";handleFailedMessages(finalError)}))}))}};const previewText=function(text){let plaintext=text.replace(//gi,"");return plaintext=plaintext.replace(//gi,""),plaintext=plaintext.replace(/<\/div>/gi,"\n"),plaintext=plaintext.replace(/<\/li>/gi,"\n"),plaintext=plaintext.replace(/
  • /gi," * "),plaintext=plaintext.replace(/<\/ul>/gi,"\n"),plaintext=plaintext.replace(/<\/p>/gi,"\n"),plaintext=plaintext.replace(/]*>/gi,"\n"),plaintext=plaintext.replace(/<[^>]+>/gi,""),plaintext=plaintext.replace(/\n+/gi,"\n"),plaintext.replace(/\n/gi,"
    ")};var processRenderBuffer=function(header,body,footer){if(!isRendering&&renderBuffer.length){isRendering=!0;var renderable=renderBuffer.shift(),renderPromises=renderers.map((function(renderFunc){return renderFunc(renderable.patch)}));$.when.apply(null,renderPromises).then((function(){isRendering=!1,renderable.deferred.resolve(!0),processRenderBuffer(header,body,footer)})).catch((function(error){isRendering=!1,renderable.deferred.reject(error),Notification.exception(error)}))}},generateConfirmActionHandler=function(actionCallback){return function(e,data){if(!viewState.loadingConfirmAction){actionCallback(getOtherUserId());var newState=StateManager.setLoadingConfirmAction(viewState,!1);render(newState)}data.originalEvent.preventDefault()}},handleSendMessage=function(e,data){var textArea=$(e.target).closest(SELECTORS.FOOTER_CONTAINER).find(SELECTORS.MESSAGE_TEXT_AREA),text=textArea.val().trim();""!==text&&(!function(text){var id="temp"+Date.now();let loadingmessage={id:id,useridfrom:viewState.loggedInUserId,text:previewText(text),timecreated:null};var newState=StateManager.addMessages(viewState,[loadingmessage]);render(newState);var message={id:id,useridfrom:viewState.loggedInUserId,text:text,timecreated:null};sendMessageBuffer.push(message),processSendMessageBuffer();const textArea=document.querySelector(SELECTORS.MESSAGE_TEXT_AREA);textArea&&textArea.removeAttribute("data-unsent-message-viewed")}(text),textArea.val(""),textArea.focus()),data.originalEvent.preventDefault()},handleSelectMessage=function(e,data){var selection=window.getSelection(),target=$(e.target);""==selection.toString()&&(target.is("a")||(!function(messageId){var newState=viewState;newState=viewState.selectedMessageIds.indexOf(messageId)>-1?StateManager.removeSelectedMessagesById(viewState,[messageId]):StateManager.addSelectedMessagesById(viewState,[messageId]),render(newState)}(target.closest(SELECTORS.MESSAGE).attr("data-message-id")),data.originalEvent.preventDefault()))},handleRetrySendMessage=function(e,data){var messageId=$(e.target).closest(SELECTORS.MESSAGE).attr("data-message-id"),messages=viewState.messages.filter((function(message){return message.id==messageId})),message=messages.length?messages[0]:null;message&&function(message){var newState=StateManager.setMessagesSendPendingById(viewState,[message.id]);render(newState),sendMessageBuffer.push(message),processSendMessageBuffer()}(message),data.originalEvent.preventDefault(),data.originalEvent.stopPropagation(),e.stopPropagation()},handleCancelEditMode=function(e,data){!function(){cancelRequest(getOtherUserId());var newState=StateManager.removeSelectedMessagesById(viewState,viewState.selectedMessageIds);render(newState)}(),data.originalEvent.preventDefault()},generateHandleViewContact=function(namespace){return function(e,data){var otherUserId=getOtherUserId(),otherUser=viewState.members[otherUserId];MessageDrawerRouter.go(namespace,MessageDrawerRoutes.VIEW_CONTACT,otherUser),data.originalEvent.preventDefault()}},handleSetFavourite=function(e,data){var userId,conversationId,pendingPromise;(userId=viewState.loggedInUserId,conversationId=viewState.id,pendingPromise=new Pending("core_message/message_drawer_view_conversation:setFavourite"),Repository.setFavouriteConversations(userId,[conversationId]).then((function(){var newState=StateManager.setIsFavourite(viewState,!0);return render(newState)})).then((function(){return PubSub.publish(MessageDrawerEvents.CONVERSATION_SET_FAVOURITE,formatConversationForEvent(viewState))})).then((function(result){return pendingPromise.resolve(),result}))).catch(Notification.exception),data.originalEvent.preventDefault()},handleUnsetFavourite=function(e,data){var userId,conversationId,pendingPromise;(userId=viewState.loggedInUserId,conversationId=viewState.id,pendingPromise=new Pending("core_message/message_drawer_view_conversation:unsetFavourite"),Repository.unsetFavouriteConversations(userId,[conversationId]).then((function(){var newState=StateManager.setIsFavourite(viewState,!1);return render(newState)})).then((function(){return PubSub.publish(MessageDrawerEvents.CONVERSATION_UNSET_FAVOURITE,formatConversationForEvent(viewState))})).then((function(result){return pendingPromise.resolve(),result}))).catch(Notification.exception),data.originalEvent.preventDefault()},handleSetMuted=function(e,data){var userId,conversationId,pendingPromise;(userId=viewState.loggedInUserId,conversationId=viewState.id,pendingPromise=new Pending("core_message/message_drawer_view_conversation:markConversationAsRead"),Repository.setMutedConversations(userId,[conversationId]).then((function(){var newState=StateManager.setIsMuted(viewState,!0);return render(newState)})).then((function(){return PubSub.publish(MessageDrawerEvents.CONVERSATION_SET_MUTED,formatConversationForEvent(viewState))})).then((function(result){return pendingPromise.resolve(),result}))).catch(Notification.exception),data.originalEvent.preventDefault()},handleUnsetMuted=function(e,data){var userId,conversationId;(userId=viewState.loggedInUserId,conversationId=viewState.id,Repository.unsetMutedConversations(userId,[conversationId]).then((function(){var newState=StateManager.setIsMuted(viewState,!1);return render(newState)})).then((function(){return PubSub.publish(MessageDrawerEvents.CONVERSATION_UNSET_MUTED,formatConversationForEvent(viewState))}))).catch(Notification.exception),data.originalEvent.preventDefault()},handleDeleteMessagesForAllUsersToggle=function(e){var newValue=$(e.target).prop("checked"),newState=StateManager.setDeleteMessagesForAllUsers(viewState,newValue);render(newState)},generateHandleViewGroupInfo=function(namespace){return function(e,data){MessageDrawerRouter.go(namespace,MessageDrawerRoutes.VIEW_GROUP_INFO,{id:viewState.id,name:viewState.name,subname:viewState.subname,imageUrl:viewState.imageUrl,totalMemberCount:viewState.totalMemberCount},viewState.loggedInUserId),data.originalEvent.preventDefault()}},handleToggleEmojiPicker=function(e,data){var newState=StateManager.setShowEmojiPicker(viewState,!viewState.showEmojiPicker);render(newState),data.originalEvent.preventDefault()},handleCloseEmojiPicker=function(e){var target=$(e.target);if(viewState.showEmojiPicker&&!target.closest(SELECTORS.EMOJI_PICKER_CONTAINER).length&&!target.closest(SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON).length){var newState=StateManager.setShowEmojiPicker(viewState,!1);render(newState)}},registerEventListeners=function(namespace,header,body,footer){var isLoadingMoreMessages=!1,messagesContainer=function(body){return body.find(SELECTORS.MESSAGES_CONTAINER)}(body),emojiPickerElement=footer.find(SELECTORS.EMOJI_PICKER),emojiAutoCompleteContainer=footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER),messageTextArea=footer.find(SELECTORS.MESSAGE_TEXT_AREA),headerActivateHandlers=[[SELECTORS.ACTION_REQUEST_BLOCK,generateConfirmActionHandler(requestBlockUser)],[SELECTORS.ACTION_REQUEST_UNBLOCK,generateConfirmActionHandler(requestUnblockUser)],[SELECTORS.ACTION_REQUEST_ADD_CONTACT,generateConfirmActionHandler(requestAddContact)],[SELECTORS.ACTION_REQUEST_REMOVE_CONTACT,generateConfirmActionHandler(requestRemoveContact)],[SELECTORS.ACTION_REQUEST_DELETE_CONVERSATION,generateConfirmActionHandler(requestDeleteConversation)],[SELECTORS.ACTION_CANCEL_EDIT_MODE,handleCancelEditMode],[SELECTORS.ACTION_VIEW_CONTACT,generateHandleViewContact(namespace)],[SELECTORS.ACTION_VIEW_GROUP_INFO,generateHandleViewGroupInfo(namespace)],[SELECTORS.ACTION_CONFIRM_FAVOURITE,handleSetFavourite],[SELECTORS.ACTION_CONFIRM_MUTE,handleSetMuted],[SELECTORS.ACTION_CONFIRM_UNFAVOURITE,handleUnsetFavourite],[SELECTORS.ACTION_CONFIRM_UNMUTE,handleUnsetMuted]],bodyActivateHandlers=[[SELECTORS.ACTION_CANCEL_CONFIRM,generateConfirmActionHandler(cancelRequest)],[SELECTORS.ACTION_CONFIRM_BLOCK,generateConfirmActionHandler(blockUser)],[SELECTORS.ACTION_CONFIRM_UNBLOCK,generateConfirmActionHandler(unblockUser)],[SELECTORS.ACTION_CONFIRM_ADD_CONTACT,generateConfirmActionHandler(addContact)],[SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT,generateConfirmActionHandler(removeContact)],[SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES,generateConfirmActionHandler(deleteSelectedMessages)],[SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION,generateConfirmActionHandler(deleteConversation)],[SELECTORS.ACTION_OKAY_CONFIRM,generateConfirmActionHandler(cancelRequest)],[SELECTORS.ACTION_REQUEST_ADD_CONTACT,generateConfirmActionHandler(requestAddContact)],[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.RETRY_SEND,handleRetrySendMessage]],footerActivateHandlers=[[SELECTORS.SEND_MESSAGE_BUTTON,handleSendMessage],[SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON,handleToggleEmojiPicker],[SELECTORS.ACTION_REQUEST_DELETE_SELECTED_MESSAGES,generateConfirmActionHandler(requestDeleteSelectedMessages)],[SELECTORS.ACTION_REQUEST_ADD_CONTACT,generateConfirmActionHandler(requestAddContact)],[SELECTORS.ACTION_REQUEST_UNBLOCK,generateConfirmActionHandler(requestUnblockUser)]];AutoRows.init(footer),emojiAutoCompleteContainer.length&&initialiseEmojiAutoComplete(emojiAutoCompleteContainer[0],messageTextArea[0],(function(hasSuggestions){var newState=StateManager.setShowEmojiAutoComplete(viewState,hasSuggestions);render(newState)}),(function(emoji){var newState=StateManager.setShowEmojiAutoComplete(viewState,!1);render(newState),messageTextArea.focus();var cursorPos=messageTextArea.prop("selectionStart"),currentText=messageTextArea.val(),textBefore=currentText.substring(0,cursorPos).replace(/\S*$/,""),textAfter=currentText.substring(cursorPos).replace(/^\S*/,"");messageTextArea.val(textBefore+emoji+textAfter),messageTextArea.prop("selectionStart",textBefore.length+emoji.length),messageTextArea.prop("selectionEnd",textBefore.length+emoji.length)})),emojiPickerElement.length&&initialiseEmojiPicker(emojiPickerElement[0],(function(emoji){var newState=StateManager.setShowEmojiPicker(viewState,!viewState.showEmojiPicker);render(newState),messageTextArea.focus();var cursorPos=messageTextArea.prop("selectionStart"),currentText=messageTextArea.val(),textBefore=currentText.substring(0,cursorPos),textAfter=currentText.substring(cursorPos,currentText.length);messageTextArea.val(textBefore+emoji+textAfter),messageTextArea.prop("selectionStart",cursorPos+emoji.length),messageTextArea.prop("selectionEnd",cursorPos+emoji.length)})),CustomEvents.define(header,[CustomEvents.events.activate]),CustomEvents.define(body,[CustomEvents.events.activate]),CustomEvents.define(footer,[CustomEvents.events.activate,CustomEvents.events.enter,CustomEvents.events.escape]),CustomEvents.define(messagesContainer,[CustomEvents.events.scrollTop,CustomEvents.events.scrollLock]),messagesContainer.on(CustomEvents.events.scrollTop,(function(e,data){var hasMembers=Object.keys(viewState.members).length>1;if(!isResetting&&!isLoadingMoreMessages&&!hasLoadedAllMessages()&&hasMembers){isLoadingMoreMessages=!0;var newState=StateManager.setLoadingMessages(viewState,!0);render(newState),loadMessages(viewState.id,LOAD_MESSAGE_LIMIT,getMessagesOffset(),NEWEST_FIRST,[]).then((function(){isLoadingMoreMessages=!1,setMessagesOffset(getMessagesOffset()+LOAD_MESSAGE_LIMIT)})).catch((function(error){isLoadingMoreMessages=!1,Notification.exception(error)}))}data.originalEvent.preventDefault()})),headerActivateHandlers.forEach((function(handler){var selector=handler[0],handlerFunction=handler[1];header.on(CustomEvents.events.activate,selector,handlerFunction)})),bodyActivateHandlers.forEach((function(handler){var selector=handler[0],handlerFunction=handler[1];body.on(CustomEvents.events.activate,selector,handlerFunction)})),footerActivateHandlers.forEach((function(handler){var selector=handler[0],handlerFunction=handler[1];footer.on(CustomEvents.events.activate,selector,handlerFunction)})),footer.on(CustomEvents.events.enter,SELECTORS.MESSAGE_TEXT_AREA,(function(e,data){var enterToSend=footer.attr("data-enter-to-send");enterToSend&&"false"!=enterToSend&&"0"!=enterToSend&&handleSendMessage(e,data)})),footer.on(CustomEvents.events.escape,SELECTORS.EMOJI_PICKER_CONTAINER,handleToggleEmojiPicker),$(document.body).on("click",handleCloseEmojiPicker),PubSub.subscribe(MessageDrawerEvents.ROUTE_CHANGED,(function(newRouteData){newMessagesPollTimer&&newRouteData.route!=MessageDrawerRoutes.VIEW_CONVERSATION&&newMessagesPollTimer.stop()}))},resetMessagePollTimer=function(conversationId){newMessagesPollTimer&&newMessagesPollTimer.stop(),newMessagesPollTimer=new BackOffTimer(function(conversationId,newestFirst){return function(){var messages=viewState.messages,mostRecentMessage=messages.length?messages[messages.length-1]:null,lastTimeCreated=mostRecentMessage?mostRecentMessage.timeCreated:null;if(lastTimeCreated&&!isResetting&&!isSendingMessage&&!isDeletingConversationContent){for(var ignoreMessageIds=[],i=messages.length-1;i>=0;i--){var message=messages[i];if(message.timeCreated!==lastTimeCreated)break;ignoreMessageIds.push(message.id)}return loadMessages(conversationId,0,0,newestFirst,ignoreMessageIds,lastTimeCreated).then((function(result){if(result.messages.length){newMessagesPollTimer.restart();var conversation=formatConversationForEvent(viewState);return PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE,conversation),markConversationAsRead(conversationId)}return result}))}return $.Deferred().resolve().promise()}}(conversationId,NEWEST_FIRST),BackOffTimer.getIncrementalCallback(viewState.messagePollMin*MILLISECONDS_IN_SEC,MILLISECONDS_IN_SEC,viewState.messagePollMax*MILLISECONDS_IN_SEC,viewState.messagePollAfterMax*MILLISECONDS_IN_SEC)),newMessagesPollTimer.start()},resetState=function(body,conversationId,loggedInUserProfile){newMessagesPollTimer&&newMessagesPollTimer.stop(),loadedAllMessages=!1,messagesOffset=0,newMessagesPollTimer=null,isRendering=!1,renderBuffer=[],isResetting=!0,isSendingMessage=!1,isDeletingConversationContent=!1,sendMessageBuffer=[];var loggedInUserId=loggedInUserProfile.id,midnight=parseInt(body.attr("data-midnight"),10),messagePollMin=parseInt(body.attr("data-message-poll-min"),10),messagePollMax=parseInt(body.attr("data-message-poll-max"),10),messagePollAfterMax=parseInt(body.attr("data-message-poll-after-max"),10),initialState=StateManager.buildInitialState(midnight,loggedInUserId,conversationId,messagePollMin,messagePollMax,messagePollAfterMax);viewState||(viewState=initialState),render(initialState)},resetNoConversation=function(body,loggedInUserProfile,otherUserId){resetState(body,null,loggedInUserProfile);return(loggedInUserProfile.id!=otherUserId?Repository.getConversationBetweenUsers(loggedInUserProfile.id,otherUserId,!0,!0,0,0,LOAD_MESSAGE_LIMIT,0,NEWEST_FIRST):Repository.getSelfConversation(loggedInUserProfile.id,LOAD_MESSAGE_LIMIT,0,NEWEST_FIRST)).then((function(conversation){return resetByConversation(body,conversation,loggedInUserProfile)})).catch((function(){return function(loggedInUserProfile,otherUserId){var loggedInUserId=loggedInUserProfile.id,conversationType=loggedInUserId==otherUserId?CONVERSATION_TYPES.SELF:CONVERSATION_TYPES.PRIVATE,newState=StateManager.setLoadingMembers(viewState,!0);return newState=StateManager.setLoadingMessages(newState,!0),render(newState),Repository.getMemberInfo(loggedInUserId,[otherUserId],!0,!0).then((function(profiles){if(profiles.length)return profiles[0];throw new Error("Unable to load other user profile")})).then((function(profile){var members=conversationType==CONVERSATION_TYPES.SELF?[profile]:[profile,loggedInUserProfile],newState=StateManager.addMembers(viewState,members);return newState=StateManager.setLoadingMembers(newState,!1),newState=StateManager.setLoadingMessages(newState,!1),newState=StateManager.setName(newState,profile.fullname),newState=StateManager.setType(newState,conversationType),newState=StateManager.setImageUrl(newState,profile.profileimageurl),newState=StateManager.setTotalMemberCount(newState,members.length),render(newState),profile})).catch((function(error){var newState=StateManager.setLoadingMembers(viewState,!1);render(newState),Notification.exception(error)}))}(loggedInUserProfile,otherUserId)}))},resetById=function(body,conversationId,loggedInUserProfile){var cache=null;conversationId in stateCache&&(cache=stateCache[conversationId]),resetState(body,conversationId,loggedInUserProfile);var promise=$.Deferred().resolve({}).promise();if(cache){var newState=cache.state;newState=StateManager.setLoadingMessages(newState,!1),newState=StateManager.setLoadingMembers(newState,!1),setMessagesOffset(cache.messagesOffset),setLoadedAllMessages(cache.loadedAllMessages),render(newState)}else promise=function(conversationId,loggedInUserProfile,messageLimit,messageOffset,newestFirst){var loggedInUserId=loggedInUserProfile.id,newState=StateManager.setLoadingMembers(viewState,!0);return newState=StateManager.setLoadingMessages(newState,!0),render(newState),Repository.getConversation(loggedInUserId,conversationId,!0,!0,0,0,messageLimit+1,messageOffset,newestFirst).then((function(conversation){return conversation.messages.length>messageLimit?conversation.messages=conversation.messages.slice(1):setLoadedAllMessages(!0),setMessagesOffset(messageOffset+messageLimit),conversation})).then((function(conversation){conversation.members.filter((function(member){return member.id==loggedInUserProfile.id})).length<1&&(conversation.members=conversation.members.concat([loggedInUserProfile]));var newState=updateStateFromConversation(conversation,loggedInUserProfile.id);return newState=StateManager.setLoadingMembers(newState,!1),newState=StateManager.setLoadingMessages(newState,!1),render(newState).then((function(){return conversation}))})).then((function(){return markConversationAsRead(conversationId)})).catch((function(error){var newState=StateManager.setLoadingMembers(viewState,!1);newState=StateManager.setLoadingMessages(newState,!1),render(newState),Notification.exception(error)}))}(conversationId,loggedInUserProfile,LOAD_MESSAGE_LIMIT,0,NEWEST_FIRST);return promise.then((function(){return resetMessagePollTimer(conversationId)}))},resetByConversation=function(body,conversation,loggedInUserProfile){var cache=null;conversation.id in stateCache&&(cache=stateCache[conversation.id]),resetState(body,conversation.id,loggedInUserProfile);var promise=$.Deferred().resolve({}).promise();if(cache){var newState=cache.state;newState=StateManager.setLoadingMessages(newState,!1),newState=StateManager.setLoadingMembers(newState,!1),setMessagesOffset(cache.messagesOffset),setLoadedAllMessages(cache.loadedAllMessages),render(newState)}else promise=function(conversation,loggedInUserProfile,messageLimit,newestFirst){conversation.members.filter((function(member){return member.id==loggedInUserProfile.id})).length<1&&(conversation.members=conversation.members.concat([loggedInUserProfile]));var messageCount=conversation.messages.length,hasLoadedEnoughMessages=messageCount>=messageLimit,newState=updateStateFromConversation(conversation,loggedInUserProfile.id);return newState=StateManager.setLoadingMembers(newState,!1),newState=StateManager.setLoadingMessages(newState,!hasLoadedEnoughMessages),render(newState).then((function(){return hasLoadedEnoughMessages?{messages:conversation.messages}:loadMessages(conversation.id,messageLimit,messageCount,newestFirst,[])})).then((function(){var messages=viewState.messages;return setMessagesOffset(messages.length),markConversationAsRead(viewState.id),messages})).catch(Notification.exception)}(conversation,loggedInUserProfile,LOAD_MESSAGE_LIMIT,NEWEST_FIRST);return promise.then((function(){return resetMessagePollTimer(conversation.id)}))};const setConversationAttributes=function(element,conversationId,otherUserId){element.removeAttr("data-conversation-id"),element.removeAttr("data-other-user-id"),conversationId?element.attr("data-conversation-id",conversationId):otherUserId&&element.attr("data-other-user-id",otherUserId)};return{show:function(namespace,header,body,footer,conversationOrId,action,otherUserId){var userId,conversation=null,conversationId=null;conversationOrId&&null!==conversationOrId&&"object"==typeof conversationOrId?(conversation=conversationOrId,conversationId=parseInt(conversation.id,10)):(conversation=null,conversationId=parseInt(conversationOrId,10),conversationId=isNaN(conversationId)?null:conversationId),!conversationId&&action&&otherUserId&&(userId=otherUserId,conversationId=Object.keys(stateCache).reduce((function(carry,id){if(!carry){var state=stateCache[id].state;state.type!=CONVERSATION_TYPES.PUBLIC&&userId in state.members&&(carry=state.id)}return carry}),null)),setConversationAttributes(footer,conversationId,otherUserId);var isNewConversation=!viewState||viewState.id!=conversationId||otherUserId&&otherUserId!=getOtherUserId();if(body.attr("data-init")||(render=function(header,body,footer,isNewConversation){var rendererFunc=function(patch){return Renderer.render(header,body,footer,patch)};if(!isNewConversation){var initialState=StateManager.buildInitialState(viewState.midnight,viewState.loggedInUserId,viewState.id);rendererFunc(Patcher.buildPatch(initialState,viewState))}return renderers.push(rendererFunc),function(newState){var patch=Patcher.buildPatch(viewState,newState),deferred=$.Deferred();return Object.keys(patch).length?renderBuffer.push({patch:patch,deferred:deferred}):deferred.resolve(!0),viewState=newState,newState.id&&(stateCache[newState.id]={state:newState,messagesOffset:getMessagesOffset(),loadedAllMessages:hasLoadedAllMessages()}),processRenderBuffer(header,body,footer),deferred.promise()}}(header,body,footer,isNewConversation),registerEventListeners(namespace,header,body,footer),body.attr("data-init",!0)),isNewConversation){var loggedInUserProfile=function(body){return{id:parseInt(body.attr("data-user-id"),10),fullname:null,profileimageurl:null,profileimageurlsmall:null,isonline:null,showonlinestatus:null,isblocked:null,iscontact:null,isdeleted:null,canmessage:null,canmessageevenifblocked:null,requirescontact:null,contactrequests:[],cancreatecontact:null}}(body);return(conversation?resetByConversation(body,conversation,loggedInUserProfile,otherUserId):conversationId?resetById(body,conversationId,loggedInUserProfile):resetNoConversation(body,loggedInUserProfile,otherUserId)).then((function(){isResetting=!1,header.find(Constants.SELECTORS.CAN_RECEIVE_FOCUS).first().focus()})).catch((function(error){isResetting=!1,Notification.exception(error)}))}if(resetMessagePollTimer(conversationId),viewState.type==CONVERSATION_TYPES.PRIVATE&&action){var currentOtherUserId=getOtherUserId();switch(action){case"block":return requestBlockUser(currentOtherUserId);case"unblock":return requestUnblockUser(currentOtherUserId);case"add-contact":return requestAddContact(currentOtherUserId);case"remove-contact":return requestRemoveContact(currentOtherUserId)}}return $.Deferred().resolve().promise()},description:function(){return Str.get_string("messagedrawerviewconversation","core_message",viewState.name)}}})); +define("core_message/message_drawer_view_conversation",["jquery","core/auto_rows","core/backoff_timer","core/custom_interaction_events","core/notification","core/pending","core/pubsub","core/str","core_message/message_repository","core_message/message_drawer_events","core_message/message_drawer_view_conversation_constants","core_message/message_drawer_view_conversation_patcher","core_message/message_drawer_view_conversation_renderer","core_message/message_drawer_view_conversation_state_manager","core_message/message_drawer_router","core_message/message_drawer_routes","core/emoji/auto_complete","core/emoji/picker"],(function($,AutoRows,BackOffTimer,CustomEvents,Notification,Pending,PubSub,Str,Repository,MessageDrawerEvents,Constants,Patcher,Renderer,StateManager,MessageDrawerRouter,MessageDrawerRoutes,initialiseEmojiAutoComplete,initialiseEmojiPicker){var stateCache={},viewState=null,loadedAllMessages=!1,messagesOffset=0,newMessagesPollTimer=null,isRendering=!1,renderBuffer=[],isResetting=!0,isSendingMessage=!1,isDeletingConversationContent=!1,sendMessageBuffer=[],render=null,renderers=[],NEWEST_FIRST=Constants.NEWEST_MESSAGES_FIRST,LOAD_MESSAGE_LIMIT=Constants.LOAD_MESSAGE_LIMIT,MILLISECONDS_IN_SEC=Constants.MILLISECONDS_IN_SEC,SELECTORS=Constants.SELECTORS,CONVERSATION_TYPES=Constants.CONVERSATION_TYPES,getOtherUserId=function(){if(!viewState||viewState.type==CONVERSATION_TYPES.PUBLIC)return null;var loggedInUserId=viewState.loggedInUserId;if(viewState.type==CONVERSATION_TYPES.SELF)return loggedInUserId;var otherUserIds=Object.keys(viewState.members).filter((function(userId){return loggedInUserId!=userId}));return otherUserIds.length?otherUserIds[0]:null},getMessagesOffset=function(){return messagesOffset},setMessagesOffset=function(value){messagesOffset=value,stateCache[viewState.id].messagesOffset=value},hasLoadedAllMessages=function(){return loadedAllMessages},setLoadedAllMessages=function(value){loadedAllMessages=value,stateCache[viewState.id].loadedAllMessages=value},formatConversationForEvent=function(state){return{id:state.id,name:state.name,subname:state.subname,imageUrl:state.imageUrl,isFavourite:state.isFavourite,isMuted:state.isMuted,type:state.type,totalMemberCount:state.totalMemberCount,loggedInUserId:state.loggedInUserId,messages:state.messages.map((function(message){return $.extend({},message)})),members:Object.keys(state.members).map((function(id){var formattedMember=$.extend({},state.members[id]);return formattedMember.contactrequests=state.members[id].contactrequests.map((function(request){return $.extend({},request)})),formattedMember}))}},updateStateFromConversation=function(conversation,loggedInUserId){var otherUser=null;if(conversation.type==CONVERSATION_TYPES.PRIVATE){var otherUsers=conversation.members.filter((function(member){return member.id!=loggedInUserId}));otherUser=otherUsers.length?otherUsers[0]:null}else conversation.type==CONVERSATION_TYPES.SELF&&(otherUser=conversation.members[0]);var name=conversation.name,imageUrl=conversation.imageurl;conversation.type!=CONVERSATION_TYPES.PUBLIC&&(name=name||otherUser?otherUser.fullname:"",imageUrl=imageUrl||otherUser?otherUser.profileimageurl:"");var newState=StateManager.addMembers(viewState,conversation.members);return newState=StateManager.setName(newState,name),newState=StateManager.setSubname(newState,conversation.subname),newState=StateManager.setType(newState,conversation.type),newState=StateManager.setImageUrl(newState,imageUrl),newState=StateManager.setTotalMemberCount(newState,conversation.membercount),newState=StateManager.setIsFavourite(newState,conversation.isfavourite),newState=StateManager.setIsMuted(newState,conversation.ismuted),newState=StateManager.addMessages(newState,conversation.messages),newState=StateManager.setCanDeleteMessagesForAllUsers(newState,conversation.candeletemessagesforallusers),newState=StateManager.setCanSendMessageToConversation(newState,conversation.cansendmessagetoconversation)},loadMessages=function(conversationId,limit,offset,newestFirst,ignoreList,timeFrom){return Repository.getMessages(viewState.loggedInUserId,conversationId,limit?limit+1:limit,offset,newestFirst,timeFrom).then((function(result){return result.id!=viewState.id&&(result.messages=[],result.id in stateCache&&delete stateCache[result.id]),result})).then((function(result){return result.messages.length&&ignoreList.length&&(result.messages=result.messages.filter((function(message){return ignoreList.indexOf(parseInt(message.id,10))<0}))),result})).then((function(result){return limit?(result.messages.length>limit?result.messages=result.messages.slice(0,-1):setLoadedAllMessages(!0),result):result})).then((function(result){var membersToAdd=result.members.filter((function(member){return!(member.id in viewState.members)})),newState=StateManager.addMembers(viewState,membersToAdd);return newState=StateManager.addMessages(newState,result.messages),newState=StateManager.setLoadingMessages(newState,!1),render(newState).then((function(){return result}))})).catch((function(error){var newState=StateManager.setLoadingMessages(viewState,!1);throw render(newState),error}))},markConversationAsRead=function(conversationId){var loggedInUserId=viewState.loggedInUserId,pendingPromise=new Pending("core_message/message_drawer_view_conversation:markConversationAsRead");return Repository.markAllConversationMessagesAsRead(loggedInUserId,conversationId).then((function(){var newState=StateManager.markMessagesAsRead(viewState,viewState.messages);return PubSub.publish(MessageDrawerEvents.CONVERSATION_READ,conversationId),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestBlockUser=function(userId){cancelRequest(userId);var newState=StateManager.addPendingBlockUsersById(viewState,[userId]);render(newState)},blockUser=function(userId){var newState=StateManager.setLoadingConfirmAction(viewState,!0),pendingPromise=new Pending("core_message/message_drawer_view_conversation:blockUser");return render(newState),Repository.blockUser(viewState.loggedInUserId,userId).then((function(profile){var newState=StateManager.addMembers(viewState,[profile]);return newState=StateManager.removePendingBlockUsersById(newState,[userId]),newState=StateManager.setLoadingConfirmAction(newState,!1),PubSub.publish(MessageDrawerEvents.CONTACT_BLOCKED,userId),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestUnblockUser=function(userId){cancelRequest(userId);var newState=StateManager.addPendingUnblockUsersById(viewState,[userId]);render(newState)},unblockUser=function(userId){var newState=StateManager.setLoadingConfirmAction(viewState,!0),pendingPromise=new Pending("core_message/message_drawer_view_conversation:unblockUser");return render(newState),Repository.unblockUser(viewState.loggedInUserId,userId).then((function(profile){var newState=StateManager.addMembers(viewState,[profile]);return newState=StateManager.removePendingUnblockUsersById(newState,[userId]),newState=StateManager.setLoadingConfirmAction(newState,!1),PubSub.publish(MessageDrawerEvents.CONTACT_UNBLOCKED,userId),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestRemoveContact=function(userId){cancelRequest(userId);var newState=StateManager.addPendingRemoveContactsById(viewState,[userId]);render(newState)},removeContact=function(userId){var newState=StateManager.setLoadingConfirmAction(viewState,!0),pendingPromise=new Pending("core_message/message_drawer_view_conversation:removeContact");return render(newState),Repository.deleteContacts(viewState.loggedInUserId,[userId]).then((function(profiles){var newState=StateManager.addMembers(viewState,profiles);return newState=StateManager.removePendingRemoveContactsById(newState,[userId]),newState=StateManager.setLoadingConfirmAction(newState,!1),PubSub.publish(MessageDrawerEvents.CONTACT_REMOVED,userId),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestAddContact=function(userId){cancelRequest(userId);var newState=StateManager.addPendingAddContactsById(viewState,[userId]);render(newState)},addContact=function(userId){var newState=StateManager.setLoadingConfirmAction(viewState,!0),pendingPromise=new Pending("core_message/message_drawer_view_conversation:addContactRequests");return render(newState),Repository.createContactRequest(viewState.loggedInUserId,userId).then((function(response){if(!response.request)throw new Error(response.warnings[0].message);return response.request})).then((function(request){var newState=StateManager.removePendingAddContactsById(viewState,[userId]);return newState=StateManager.addContactRequests(newState,[request]),newState=StateManager.setLoadingConfirmAction(newState,!1),render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},requestDeleteSelectedMessages=function(userId){var selectedMessageIds=viewState.selectedMessageIds;cancelRequest(userId);var newState=StateManager.addPendingDeleteMessagesById(viewState,selectedMessageIds);render(newState)},deleteSelectedMessages=function(){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:deleteSelectedMessages"),messageIds=viewState.pendingDeleteMessageIds,sentMessages=viewState.messages.filter((function(message){return messageIds.indexOf(message.id)>=0&&("sent"==message.sendState||null===message.sendState)})),newState=StateManager.setLoadingConfirmAction(viewState,!0);render(newState);var deleteMessagesPromise=$.Deferred().resolve().promise();if(sentMessages.length){var sentMessageIds=sentMessages.map((function(message){return message.id}));deleteMessagesPromise=newState.deleteMessagesForAllUsers?Repository.deleteMessagesForAllUsers(viewState.loggedInUserId,sentMessageIds):Repository.deleteMessages(viewState.loggedInUserId,sentMessageIds)}return isDeletingConversationContent=!0,newMessagesPollTimer&&newMessagesPollTimer.stop(),deleteMessagesPromise.then((function(){var newState=StateManager.removeMessagesById(viewState,messageIds);newState=StateManager.removePendingDeleteMessagesById(newState,messageIds),newState=StateManager.removeSelectedMessagesById(newState,messageIds),newState=StateManager.setLoadingConfirmAction(newState,!1),newState=StateManager.setDeleteMessagesForAllUsers(newState,!1);var prevLastMessage=viewState.messages[viewState.messages.length-1],newLastMessage=newState.messages.length?newState.messages[newState.messages.length-1]:null;if(newLastMessage&&newLastMessage.id!=prevLastMessage.id){var conversation=formatConversationForEvent(newState);PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE,conversation)}else newState.messages.length||PubSub.publish(MessageDrawerEvents.CONVERSATION_DELETED,newState.id);return isDeletingConversationContent=!1,render(newState)})).then((function(result){return pendingPromise.resolve(),result})).catch(Notification.exception)},requestDeleteConversation=function(userId){cancelRequest(userId);var newState=StateManager.setPendingDeleteConversation(viewState,!0);render(newState)},deleteConversation=function(){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:markConversationAsRead"),newState=StateManager.setLoadingConfirmAction(viewState,!0);return render(newState),isDeletingConversationContent=!0,newMessagesPollTimer&&newMessagesPollTimer.stop(),Repository.deleteConversation(viewState.loggedInUserId,viewState.id).then((function(){var newState=StateManager.removeMessages(viewState,viewState.messages);return newState=StateManager.removeSelectedMessagesById(newState,viewState.selectedMessageIds),newState=StateManager.setPendingDeleteConversation(newState,!1),newState=StateManager.setLoadingConfirmAction(newState,!1),PubSub.publish(MessageDrawerEvents.CONVERSATION_DELETED,newState.id),isDeletingConversationContent=!1,render(newState)})).then((function(result){return pendingPromise.resolve(),result}))},cancelRequest=function(userId){var pendingDeleteMessageIds=viewState.pendingDeleteMessageIds,newState=StateManager.removePendingAddContactsById(viewState,[userId]);newState=StateManager.removePendingRemoveContactsById(newState,[userId]),newState=StateManager.removePendingUnblockUsersById(newState,[userId]),newState=StateManager.removePendingBlockUsersById(newState,[userId]),newState=StateManager.removePendingDeleteMessagesById(newState,pendingDeleteMessageIds),newState=StateManager.setPendingDeleteConversation(newState,!1),newState=StateManager.setDeleteMessagesForAllUsers(newState,!1),render(newState)},acceptContactRequest=function(userId){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:acceptContactRequest"),loggedInUserId=viewState.loggedInUserId,requests=viewState.members[userId].contactrequests.filter((function(request){return request.requesteduserid==loggedInUserId})),request=requests[0],newState=StateManager.setLoadingConfirmAction(viewState,!0);return render(newState),Repository.acceptContactRequest(userId,loggedInUserId).then((function(profile){var newState=StateManager.removeContactRequests(viewState,[request]);return newState=StateManager.addMembers(viewState,[profile]),newState=StateManager.setLoadingConfirmAction(newState,!1),render(newState)})).then((function(){PubSub.publish(MessageDrawerEvents.CONTACT_ADDED,viewState.members[userId]),PubSub.publish(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED,request)})).then((function(result){return pendingPromise.resolve(),result}))},declineContactRequest=function(userId){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:declineContactRequest"),loggedInUserId=viewState.loggedInUserId,requests=viewState.members[userId].contactrequests.filter((function(request){return request.requesteduserid==loggedInUserId})),request=requests[0],newState=StateManager.setLoadingConfirmAction(viewState,!0);return render(newState),Repository.declineContactRequest(userId,loggedInUserId).then((function(profile){var newState=StateManager.removeContactRequests(viewState,[request]);return newState=StateManager.addMembers(viewState,[profile]),newState=StateManager.setLoadingConfirmAction(newState,!1),render(newState)})).then((function(){PubSub.publish(MessageDrawerEvents.CONTACT_REQUEST_DECLINED,request)})).then((function(result){return pendingPromise.resolve(),result}))},processSendMessageBuffer=function(){if(!isSendingMessage&&sendMessageBuffer.length){var pendingPromise=new Pending("core_message/message_drawer_view_conversation:processSendMessageBuffer");isSendingMessage=!0;var messagesToSend=sendMessageBuffer.slice();sendMessageBuffer=[];var conversationId=viewState.id,newConversationId=null,messagesText=messagesToSend.map((function(message){return message.text})),messageIds=messagesToSend.map((function(message){return message.id})),sendMessagePromise=null,newCanDeleteMessagesForAllUsers=null;if(conversationId||viewState.type==CONVERSATION_TYPES.PUBLIC)sendMessagePromise=Repository.sendMessagesToConversation(conversationId,messagesText);else{var otherUserId=getOtherUserId();sendMessagePromise=Repository.sendMessagesToUser(otherUserId,messagesText).then((function(messages){return messages.length&&(newConversationId=parseInt(messages[0].conversationid,10),newCanDeleteMessagesForAllUsers=messages[0].candeletemessagesforallusers),messages}))}sendMessagePromise.then((function(messages){var newMessageIds=messages.map((function(message){return message.id})),data=[],selectedToRemove=[],selectedToAdd=[];messagesToSend.forEach((function(oldMessage,index){var newMessage=messages[index];data.push([oldMessage,newMessage]),viewState.selectedMessageIds.indexOf(oldMessage.id)>=0&&(selectedToRemove.push(oldMessage.id),selectedToAdd.push(newMessage.id))}));var newState=StateManager.updateMessages(viewState,data);newState=StateManager.setMessagesSendSuccessById(newState,newMessageIds),selectedToRemove.length&&(newState=StateManager.removeSelectedMessagesById(newState,selectedToRemove)),selectedToAdd.length&&(newState=StateManager.addSelectedMessagesById(newState,selectedToAdd));var conversation=formatConversationForEvent(newState);newState.id||(newState=StateManager.setId(newState,newConversationId),conversation.id=newConversationId,resetMessagePollTimer(newConversationId),PubSub.publish(MessageDrawerEvents.CONVERSATION_CREATED,conversation),newState=StateManager.setCanDeleteMessagesForAllUsers(newState,newCanDeleteMessagesForAllUsers)),render(newState),isSendingMessage=!1,processSendMessageBuffer(),PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE,conversation)})).then((function(result){return pendingPromise.resolve(),result})).catch((function(e){var errorMessage;errorMessage=e.message?$.Deferred().resolve(e.message).promise():Str.get_string("unknownerror","core");var handleFailedMessages=function(errorMessage){var newState=StateManager.setMessagesSendFailById(viewState,messageIds,errorMessage);render(newState),isSendingMessage=!1,processSendMessageBuffer()};errorMessage.then(handleFailedMessages).then((function(result){return pendingPromise.resolve(),result})).catch((function(e){var finalError=e.message||"Something went wrong!";handleFailedMessages(finalError)}))}))}};const previewText=function(text){let plaintext=text.replace(//gi,"");return plaintext=plaintext.replace(//gi,""),plaintext=plaintext.replace(/<\/div>/gi,"\n"),plaintext=plaintext.replace(/<\/li>/gi,"\n"),plaintext=plaintext.replace(/
  • /gi," * "),plaintext=plaintext.replace(/<\/ul>/gi,"\n"),plaintext=plaintext.replace(/<\/p>/gi,"\n"),plaintext=plaintext.replace(/]*>/gi,"\n"),plaintext=plaintext.replace(/<[^>]+>/gi,""),plaintext=plaintext.replace(/\n+/gi,"\n"),plaintext.replace(/\n/gi,"
    ")};var processRenderBuffer=function(header,body,footer){if(!isRendering&&renderBuffer.length){isRendering=!0;var renderable=renderBuffer.shift(),renderPromises=renderers.map((function(renderFunc){return renderFunc(renderable.patch)}));$.when.apply(null,renderPromises).then((function(){isRendering=!1,renderable.deferred.resolve(!0),processRenderBuffer(header,body,footer)})).catch((function(error){isRendering=!1,renderable.deferred.reject(error),Notification.exception(error)}))}},generateConfirmActionHandler=function(actionCallback){return function(e,data){if(!viewState.loadingConfirmAction){actionCallback(getOtherUserId());var newState=StateManager.setLoadingConfirmAction(viewState,!1);render(newState)}data.originalEvent.preventDefault()}},handleSendMessage=function(e,data){var textArea=$(e.target).closest(SELECTORS.FOOTER_CONTAINER).find(SELECTORS.MESSAGE_TEXT_AREA),text=textArea.val().trim();""!==text&&(!function(text){var id="temp"+Date.now();let loadingmessage={id:id,useridfrom:viewState.loggedInUserId,text:previewText(text),timecreated:null};var newState=StateManager.addMessages(viewState,[loadingmessage]);render(newState);var message={id:id,useridfrom:viewState.loggedInUserId,text:text,timecreated:null};sendMessageBuffer.push(message),processSendMessageBuffer();const textArea=document.querySelector(SELECTORS.MESSAGE_TEXT_AREA);textArea&&textArea.removeAttribute("data-unsent-message-viewed")}(text),textArea.val(""),textArea.focus()),data.originalEvent.preventDefault()},handleSelectMessage=function(e,data){var selection=window.getSelection(),target=$(e.target);""==selection.toString()&&(target.is("a")||(!function(messageId){var newState=viewState;newState=viewState.selectedMessageIds.indexOf(messageId)>-1?StateManager.removeSelectedMessagesById(viewState,[messageId]):StateManager.addSelectedMessagesById(viewState,[messageId]),render(newState)}(target.closest(SELECTORS.MESSAGE).attr("data-message-id")),data.originalEvent.preventDefault()))},handleRetrySendMessage=function(e,data){var messageId=$(e.target).closest(SELECTORS.MESSAGE).attr("data-message-id"),messages=viewState.messages.filter((function(message){return message.id==messageId})),message=messages.length?messages[0]:null;message&&function(message){var newState=StateManager.setMessagesSendPendingById(viewState,[message.id]);render(newState),sendMessageBuffer.push(message),processSendMessageBuffer()}(message),data.originalEvent.preventDefault(),data.originalEvent.stopPropagation(),e.stopPropagation()},handleCancelEditMode=function(e,data){!function(){cancelRequest(getOtherUserId());var newState=StateManager.removeSelectedMessagesById(viewState,viewState.selectedMessageIds);render(newState)}(),data.originalEvent.preventDefault()},generateHandleViewContact=function(namespace){return function(e,data){var otherUserId=getOtherUserId(),otherUser=viewState.members[otherUserId];MessageDrawerRouter.go(namespace,MessageDrawerRoutes.VIEW_CONTACT,otherUser),data.originalEvent.preventDefault()}},handleSetFavourite=function(e,data){var userId,conversationId,pendingPromise;(userId=viewState.loggedInUserId,conversationId=viewState.id,pendingPromise=new Pending("core_message/message_drawer_view_conversation:setFavourite"),Repository.setFavouriteConversations(userId,[conversationId]).then((function(){var newState=StateManager.setIsFavourite(viewState,!0);return render(newState)})).then((function(){return PubSub.publish(MessageDrawerEvents.CONVERSATION_SET_FAVOURITE,formatConversationForEvent(viewState))})).then((function(result){return pendingPromise.resolve(),result}))).catch(Notification.exception),data.originalEvent.preventDefault()},handleUnsetFavourite=function(e,data){var userId,conversationId,pendingPromise;(userId=viewState.loggedInUserId,conversationId=viewState.id,pendingPromise=new Pending("core_message/message_drawer_view_conversation:unsetFavourite"),Repository.unsetFavouriteConversations(userId,[conversationId]).then((function(){var newState=StateManager.setIsFavourite(viewState,!1);return render(newState)})).then((function(){return PubSub.publish(MessageDrawerEvents.CONVERSATION_UNSET_FAVOURITE,formatConversationForEvent(viewState))})).then((function(result){return pendingPromise.resolve(),result}))).catch(Notification.exception),data.originalEvent.preventDefault()},handleSetMuted=function(e,data){var userId,conversationId,pendingPromise;(userId=viewState.loggedInUserId,conversationId=viewState.id,pendingPromise=new Pending("core_message/message_drawer_view_conversation:markConversationAsRead"),Repository.setMutedConversations(userId,[conversationId]).then((function(){var newState=StateManager.setIsMuted(viewState,!0);return render(newState)})).then((function(){return PubSub.publish(MessageDrawerEvents.CONVERSATION_SET_MUTED,formatConversationForEvent(viewState))})).then((function(result){return pendingPromise.resolve(),result}))).catch(Notification.exception),data.originalEvent.preventDefault()},handleUnsetMuted=function(e,data){var userId,conversationId;(userId=viewState.loggedInUserId,conversationId=viewState.id,Repository.unsetMutedConversations(userId,[conversationId]).then((function(){var newState=StateManager.setIsMuted(viewState,!1);return render(newState)})).then((function(){return PubSub.publish(MessageDrawerEvents.CONVERSATION_UNSET_MUTED,formatConversationForEvent(viewState))}))).catch(Notification.exception),data.originalEvent.preventDefault()},handleDeleteMessagesForAllUsersToggle=function(e){var newValue=$(e.target).prop("checked"),newState=StateManager.setDeleteMessagesForAllUsers(viewState,newValue);render(newState)},generateHandleViewGroupInfo=function(namespace){return function(e,data){MessageDrawerRouter.go(namespace,MessageDrawerRoutes.VIEW_GROUP_INFO,{id:viewState.id,name:viewState.name,subname:viewState.subname,imageUrl:viewState.imageUrl,totalMemberCount:viewState.totalMemberCount},viewState.loggedInUserId),data.originalEvent.preventDefault()}},handleToggleEmojiPicker=function(e,data){var newState=StateManager.setShowEmojiPicker(viewState,!viewState.showEmojiPicker);render(newState),data.originalEvent.preventDefault()},handleCloseEmojiPicker=function(e){var target=$(e.target);if(viewState.showEmojiPicker&&!target.closest(SELECTORS.EMOJI_PICKER_CONTAINER).length&&!target.closest(SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON).length){var newState=StateManager.setShowEmojiPicker(viewState,!1);render(newState)}},registerEventListeners=function(namespace,header,body,footer){var isLoadingMoreMessages=!1,messagesContainer=function(body){return body.find(SELECTORS.MESSAGES_CONTAINER)}(body),emojiPickerElement=footer.find(SELECTORS.EMOJI_PICKER),emojiAutoCompleteContainer=footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER),messageTextArea=footer.find(SELECTORS.MESSAGE_TEXT_AREA),headerActivateHandlers=[[SELECTORS.ACTION_REQUEST_BLOCK,generateConfirmActionHandler(requestBlockUser)],[SELECTORS.ACTION_REQUEST_UNBLOCK,generateConfirmActionHandler(requestUnblockUser)],[SELECTORS.ACTION_REQUEST_ADD_CONTACT,generateConfirmActionHandler(requestAddContact)],[SELECTORS.ACTION_REQUEST_REMOVE_CONTACT,generateConfirmActionHandler(requestRemoveContact)],[SELECTORS.ACTION_REQUEST_DELETE_CONVERSATION,generateConfirmActionHandler(requestDeleteConversation)],[SELECTORS.ACTION_CANCEL_EDIT_MODE,handleCancelEditMode],[SELECTORS.ACTION_VIEW_CONTACT,generateHandleViewContact(namespace)],[SELECTORS.ACTION_VIEW_GROUP_INFO,generateHandleViewGroupInfo(namespace)],[SELECTORS.ACTION_CONFIRM_FAVOURITE,handleSetFavourite],[SELECTORS.ACTION_CONFIRM_MUTE,handleSetMuted],[SELECTORS.ACTION_CONFIRM_UNFAVOURITE,handleUnsetFavourite],[SELECTORS.ACTION_CONFIRM_UNMUTE,handleUnsetMuted]],bodyActivateHandlers=[[SELECTORS.ACTION_CANCEL_CONFIRM,generateConfirmActionHandler(cancelRequest)],[SELECTORS.ACTION_CONFIRM_BLOCK,generateConfirmActionHandler(blockUser)],[SELECTORS.ACTION_CONFIRM_UNBLOCK,generateConfirmActionHandler(unblockUser)],[SELECTORS.ACTION_CONFIRM_ADD_CONTACT,generateConfirmActionHandler(addContact)],[SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT,generateConfirmActionHandler(removeContact)],[SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES,generateConfirmActionHandler(deleteSelectedMessages)],[SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION,generateConfirmActionHandler(deleteConversation)],[SELECTORS.ACTION_OKAY_CONFIRM,generateConfirmActionHandler(cancelRequest)],[SELECTORS.ACTION_REQUEST_ADD_CONTACT,generateConfirmActionHandler(requestAddContact)],[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.RETRY_SEND,handleRetrySendMessage]],footerActivateHandlers=[[SELECTORS.SEND_MESSAGE_BUTTON,handleSendMessage],[SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON,handleToggleEmojiPicker],[SELECTORS.ACTION_REQUEST_DELETE_SELECTED_MESSAGES,generateConfirmActionHandler(requestDeleteSelectedMessages)],[SELECTORS.ACTION_REQUEST_ADD_CONTACT,generateConfirmActionHandler(requestAddContact)],[SELECTORS.ACTION_REQUEST_UNBLOCK,generateConfirmActionHandler(requestUnblockUser)]];AutoRows.init(footer),emojiAutoCompleteContainer.length&&initialiseEmojiAutoComplete(emojiAutoCompleteContainer[0],messageTextArea[0],(function(hasSuggestions){var newState=StateManager.setShowEmojiAutoComplete(viewState,hasSuggestions);render(newState)}),(function(emoji){var newState=StateManager.setShowEmojiAutoComplete(viewState,!1);render(newState),messageTextArea.focus();var cursorPos=messageTextArea.prop("selectionStart"),currentText=messageTextArea.val(),textBefore=currentText.substring(0,cursorPos).replace(/\S*$/,""),textAfter=currentText.substring(cursorPos).replace(/^\S*/,"");messageTextArea.val(textBefore+emoji+textAfter),messageTextArea.prop("selectionStart",textBefore.length+emoji.length),messageTextArea.prop("selectionEnd",textBefore.length+emoji.length)})),emojiPickerElement.length&&initialiseEmojiPicker(emojiPickerElement[0],(function(emoji){var newState=StateManager.setShowEmojiPicker(viewState,!viewState.showEmojiPicker);render(newState),messageTextArea.focus();var cursorPos=messageTextArea.prop("selectionStart"),currentText=messageTextArea.val(),textBefore=currentText.substring(0,cursorPos),textAfter=currentText.substring(cursorPos,currentText.length);messageTextArea.val(textBefore+emoji+textAfter),messageTextArea.prop("selectionStart",cursorPos+emoji.length),messageTextArea.prop("selectionEnd",cursorPos+emoji.length)})),CustomEvents.define(header,[CustomEvents.events.activate]),CustomEvents.define(body,[CustomEvents.events.activate]),CustomEvents.define(footer,[CustomEvents.events.activate,CustomEvents.events.enter,CustomEvents.events.escape]),CustomEvents.define(messagesContainer,[CustomEvents.events.scrollTop,CustomEvents.events.scrollLock]),messagesContainer.on(CustomEvents.events.scrollTop,(function(e,data){var hasMembers=Object.keys(viewState.members).length>1;if(!isResetting&&!isLoadingMoreMessages&&!hasLoadedAllMessages()&&hasMembers){isLoadingMoreMessages=!0;var newState=StateManager.setLoadingMessages(viewState,!0);render(newState),loadMessages(viewState.id,LOAD_MESSAGE_LIMIT,getMessagesOffset(),NEWEST_FIRST,[]).then((function(){isLoadingMoreMessages=!1,setMessagesOffset(getMessagesOffset()+LOAD_MESSAGE_LIMIT)})).catch((function(error){isLoadingMoreMessages=!1,Notification.exception(error)}))}data.originalEvent.preventDefault()})),headerActivateHandlers.forEach((function(handler){var selector=handler[0],handlerFunction=handler[1];header.on(CustomEvents.events.activate,selector,handlerFunction)})),bodyActivateHandlers.forEach((function(handler){var selector=handler[0],handlerFunction=handler[1];body.on(CustomEvents.events.activate,selector,handlerFunction)})),footerActivateHandlers.forEach((function(handler){var selector=handler[0],handlerFunction=handler[1];footer.on(CustomEvents.events.activate,selector,handlerFunction)})),footer.on(CustomEvents.events.enter,SELECTORS.MESSAGE_TEXT_AREA,(function(e,data){var enterToSend=footer.attr("data-enter-to-send");enterToSend&&"false"!=enterToSend&&"0"!=enterToSend&&handleSendMessage(e,data)})),footer.on(CustomEvents.events.escape,SELECTORS.EMOJI_PICKER_CONTAINER,handleToggleEmojiPicker),$(document.body).on("click",handleCloseEmojiPicker),PubSub.subscribe(MessageDrawerEvents.ROUTE_CHANGED,(function(newRouteData){newMessagesPollTimer&&newRouteData.route!=MessageDrawerRoutes.VIEW_CONVERSATION&&newMessagesPollTimer.stop()}))},resetMessagePollTimer=function(conversationId){newMessagesPollTimer&&newMessagesPollTimer.stop(),newMessagesPollTimer=new BackOffTimer(function(conversationId,newestFirst){return function(){var messages=viewState.messages,mostRecentMessage=messages.length?messages[messages.length-1]:null,lastTimeCreated=mostRecentMessage?mostRecentMessage.timeCreated:null;if(lastTimeCreated&&!isResetting&&!isSendingMessage&&!isDeletingConversationContent){for(var ignoreMessageIds=[],i=messages.length-1;i>=0;i--){var message=messages[i];if(message.timeCreated!==lastTimeCreated)break;ignoreMessageIds.push(message.id)}return loadMessages(conversationId,0,0,newestFirst,ignoreMessageIds,lastTimeCreated).then((function(result){if(result.messages.length){newMessagesPollTimer.restart();var conversation=formatConversationForEvent(viewState);return PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE,conversation),markConversationAsRead(conversationId)}return result}))}return $.Deferred().resolve().promise()}}(conversationId,NEWEST_FIRST),BackOffTimer.getIncrementalCallback(viewState.messagePollMin*MILLISECONDS_IN_SEC,MILLISECONDS_IN_SEC,viewState.messagePollMax*MILLISECONDS_IN_SEC,viewState.messagePollAfterMax*MILLISECONDS_IN_SEC)),newMessagesPollTimer.start()},resetState=function(body,conversationId,loggedInUserProfile){newMessagesPollTimer&&newMessagesPollTimer.stop(),loadedAllMessages=!1,messagesOffset=0,newMessagesPollTimer=null,isRendering=!1,renderBuffer=[],isResetting=!0,isSendingMessage=!1,isDeletingConversationContent=!1,sendMessageBuffer=[];var loggedInUserId=loggedInUserProfile.id,midnight=parseInt(body.attr("data-midnight"),10),messagePollMin=parseInt(body.attr("data-message-poll-min"),10),messagePollMax=parseInt(body.attr("data-message-poll-max"),10),messagePollAfterMax=parseInt(body.attr("data-message-poll-after-max"),10),initialState=StateManager.buildInitialState(midnight,loggedInUserId,conversationId,messagePollMin,messagePollMax,messagePollAfterMax);viewState||(viewState=initialState),render(initialState)},resetNoConversation=function(body,loggedInUserProfile,otherUserId){resetState(body,null,loggedInUserProfile);return(loggedInUserProfile.id!=otherUserId?Repository.getConversationBetweenUsers(loggedInUserProfile.id,otherUserId,!0,!0,0,0,LOAD_MESSAGE_LIMIT,0,NEWEST_FIRST):Repository.getSelfConversation(loggedInUserProfile.id,LOAD_MESSAGE_LIMIT,0,NEWEST_FIRST)).then((function(conversation){return resetByConversation(body,conversation,loggedInUserProfile)})).catch((function(){return function(loggedInUserProfile,otherUserId){var loggedInUserId=loggedInUserProfile.id,conversationType=loggedInUserId==otherUserId?CONVERSATION_TYPES.SELF:CONVERSATION_TYPES.PRIVATE,newState=StateManager.setLoadingMembers(viewState,!0);return newState=StateManager.setLoadingMessages(newState,!0),render(newState),Repository.getMemberInfo(loggedInUserId,[otherUserId],!0,!0).then((function(profiles){if(profiles.length)return profiles[0];throw new Error("Unable to load other user profile")})).then((function(profile){var members=conversationType==CONVERSATION_TYPES.SELF?[profile]:[profile,loggedInUserProfile],newState=StateManager.addMembers(viewState,members);return newState=StateManager.setLoadingMembers(newState,!1),newState=StateManager.setLoadingMessages(newState,!1),newState=StateManager.setName(newState,profile.fullname),newState=StateManager.setType(newState,conversationType),newState=StateManager.setImageUrl(newState,profile.profileimageurl),newState=StateManager.setTotalMemberCount(newState,members.length),render(newState),profile})).catch((function(error){var newState=StateManager.setLoadingMembers(viewState,!1);render(newState),Notification.exception(error)}))}(loggedInUserProfile,otherUserId)}))},resetById=function(body,conversationId,loggedInUserProfile){var cache=null;conversationId in stateCache&&(cache=stateCache[conversationId]),resetState(body,conversationId,loggedInUserProfile);var promise=$.Deferred().resolve({}).promise();if(cache){var newState=cache.state;newState=StateManager.setLoadingMessages(newState,!1),newState=StateManager.setLoadingMembers(newState,!1),setMessagesOffset(cache.messagesOffset),setLoadedAllMessages(cache.loadedAllMessages),render(newState)}else promise=function(conversationId,loggedInUserProfile,messageLimit,messageOffset,newestFirst){var loggedInUserId=loggedInUserProfile.id,newState=StateManager.setLoadingMembers(viewState,!0);return newState=StateManager.setLoadingMessages(newState,!0),render(newState),Repository.getConversation(loggedInUserId,conversationId,!0,!0,0,0,messageLimit+1,messageOffset,newestFirst).then((function(conversation){return conversation.messages.length>messageLimit?conversation.messages=conversation.messages.slice(1):setLoadedAllMessages(!0),setMessagesOffset(messageOffset+messageLimit),conversation})).then((function(conversation){conversation.members.filter((function(member){return member.id==loggedInUserProfile.id})).length<1&&(conversation.members=conversation.members.concat([loggedInUserProfile]));var newState=updateStateFromConversation(conversation,loggedInUserProfile.id);return newState=StateManager.setLoadingMembers(newState,!1),newState=StateManager.setLoadingMessages(newState,!1),render(newState).then((function(){return conversation}))})).then((function(){return markConversationAsRead(conversationId)})).catch((function(error){var newState=StateManager.setLoadingMembers(viewState,!1);newState=StateManager.setLoadingMessages(newState,!1),render(newState),Notification.exception(error)}))}(conversationId,loggedInUserProfile,LOAD_MESSAGE_LIMIT,0,NEWEST_FIRST);return promise.then((function(){return resetMessagePollTimer(conversationId)}))},resetByConversation=function(body,conversation,loggedInUserProfile){var cache=null;conversation.id in stateCache&&(cache=stateCache[conversation.id]),resetState(body,conversation.id,loggedInUserProfile);var promise=$.Deferred().resolve({}).promise();if(cache){var newState=cache.state;newState=StateManager.setLoadingMessages(newState,!1),newState=StateManager.setLoadingMembers(newState,!1),setMessagesOffset(cache.messagesOffset),setLoadedAllMessages(cache.loadedAllMessages),render(newState)}else promise=function(conversation,loggedInUserProfile,messageLimit,newestFirst){conversation.members.filter((function(member){return member.id==loggedInUserProfile.id})).length<1&&(conversation.members=conversation.members.concat([loggedInUserProfile]));var messageCount=conversation.messages.length,hasLoadedEnoughMessages=messageCount>=messageLimit,newState=updateStateFromConversation(conversation,loggedInUserProfile.id);return newState=StateManager.setLoadingMembers(newState,!1),newState=StateManager.setLoadingMessages(newState,!hasLoadedEnoughMessages),render(newState).then((function(){return hasLoadedEnoughMessages?{messages:conversation.messages}:loadMessages(conversation.id,messageLimit,messageCount,newestFirst,[])})).then((function(){var messages=viewState.messages;return setMessagesOffset(messages.length),markConversationAsRead(viewState.id),messages})).catch(Notification.exception)}(conversation,loggedInUserProfile,LOAD_MESSAGE_LIMIT,NEWEST_FIRST);return promise.then((function(){return resetMessagePollTimer(conversation.id)}))};const setConversationAttributes=function(element,conversationId,otherUserId){element.removeAttr("data-conversation-id"),element.removeAttr("data-other-user-id"),conversationId?element.attr("data-conversation-id",conversationId):otherUserId&&element.attr("data-other-user-id",otherUserId)};return{show:function(namespace,header,body,footer,conversationOrId,action,otherUserId){var userId,conversation=null,conversationId=null;conversationOrId&&null!==conversationOrId&&"object"==typeof conversationOrId?(conversation=conversationOrId,conversationId=parseInt(conversation.id,10)):(conversation=null,conversationId=parseInt(conversationOrId,10),conversationId=isNaN(conversationId)?null:conversationId),!conversationId&&action&&otherUserId&&(userId=otherUserId,conversationId=Object.keys(stateCache).reduce((function(carry,id){if(!carry){var state=stateCache[id].state;state.type!=CONVERSATION_TYPES.PUBLIC&&userId in state.members&&(carry=state.id)}return carry}),null)),setConversationAttributes(footer,conversationId,otherUserId);var isNewConversation=!viewState||viewState.id!=conversationId||otherUserId&&otherUserId!=getOtherUserId();if(body.attr("data-init")||(render=function(header,body,footer,isNewConversation){var rendererFunc=function(patch){return Renderer.render(header,body,footer,patch)};if(!isNewConversation){var initialState=StateManager.buildInitialState(viewState.midnight,viewState.loggedInUserId,viewState.id);rendererFunc(Patcher.buildPatch(initialState,viewState))}return renderers.push(rendererFunc),function(newState){var patch=Patcher.buildPatch(viewState,newState),deferred=$.Deferred();return Object.keys(patch).length?renderBuffer.push({patch:patch,deferred:deferred}):deferred.resolve(!0),viewState=newState,newState.id&&(stateCache[newState.id]={state:newState,messagesOffset:getMessagesOffset(),loadedAllMessages:hasLoadedAllMessages()}),processRenderBuffer(header,body,footer),deferred.promise()}}(header,body,footer,isNewConversation),registerEventListeners(namespace,header,body,footer),body.attr("data-init",!0)),isNewConversation){var loggedInUserProfile=function(body){return{id:parseInt(body.attr("data-user-id"),10),fullname:null,profileimageurl:null,profileimageurlsmall:null,isonline:null,showonlinestatus:null,isblocked:null,iscontact:null,isdeleted:null,canmessage:null,canmessageevenifblocked:null,requirescontact:null,contactrequests:[],cancreatecontact:null}}(body);return(conversation?resetByConversation(body,conversation,loggedInUserProfile,otherUserId):conversationId?resetById(body,conversationId,loggedInUserProfile):resetNoConversation(body,loggedInUserProfile,otherUserId)).then((function(){isResetting=!1,header.find(Constants.SELECTORS.CAN_RECEIVE_FOCUS).first().focus()})).catch((function(error){isResetting=!1,Notification.exception(error)}))}if(resetMessagePollTimer(conversationId),viewState.type==CONVERSATION_TYPES.PRIVATE&&action){var currentOtherUserId=getOtherUserId();switch(action){case"block":return requestBlockUser(currentOtherUserId);case"unblock":return requestUnblockUser(currentOtherUserId);case"add-contact":return requestAddContact(currentOtherUserId);case"remove-contact":return requestRemoveContact(currentOtherUserId)}}return $.Deferred().resolve().promise()},description:function(){return Str.get_string("messagedrawerviewconversation","core_message",viewState.name)}}})); //# sourceMappingURL=message_drawer_view_conversation.min.js.map \ No newline at end of file diff --git a/public/message/amd/build/message_drawer_view_conversation.min.js.map b/public/message/amd/build/message_drawer_view_conversation.min.js.map index 6254c14a562..852a2d52be4 100644 --- a/public/message/amd/build/message_drawer_view_conversation.min.js.map +++ b/public/message/amd/build/message_drawer_view_conversation.min.js.map @@ -1 +1 @@ -{"version":3,"file":"message_drawer_view_conversation.min.js","sources":["../src/message_drawer_view_conversation.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 .\n\n/**\n * Controls the conversation page in the message drawer.\n *\n * This function handles all of the user actions that the user can take\n * when interacting with the conversation page.\n *\n * It maintains a view state which is a data representation of the view\n * and only operates on that data.\n *\n * The view state is immutable and should never be modified directly. Instead\n * all changes to the view state should be done using the StateManager which\n * will generate a new version of the view state with the requested changes.\n *\n * After any changes to the view state the module will call the render function\n * to ask the renderer to update the UI.\n *\n * General rules for this module:\n * 1.) Never modify viewState directly. All changes should be via the StateManager.\n * 2.) Call render() with the new state when you want to update the UI\n * 3.) Never modify the UI directly in this module. This module is only concerned\n * with the data in the view state.\n *\n * The general flow for a user interaction will be something like:\n * User interaction: User clicks \"confirm block\" button to block the other user\n * 1.) This module is hears the click\n * 2.) This module sends a request to the server to block the user\n * 3.) The server responds with the new user profile\n * 4.) This module generates a new state using the StateManager with the updated\n * user profile.\n * 5.) This module asks the Patcher to generate a patch from the current state and\n * the newly generated state. This patch tells the renderer what has changed\n * between the states.\n * 6.) This module gives the Renderer the generated patch. The renderer updates\n * the UI with changes according to the patch.\n *\n * @module core_message/message_drawer_view_conversation\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/auto_rows',\n 'core/backoff_timer',\n 'core/custom_interaction_events',\n 'core/notification',\n 'core/pending',\n 'core/pubsub',\n 'core/str',\n 'core_message/message_repository',\n 'core_message/message_drawer_events',\n 'core_message/message_drawer_view_conversation_constants',\n 'core_message/message_drawer_view_conversation_patcher',\n 'core_message/message_drawer_view_conversation_renderer',\n 'core_message/message_drawer_view_conversation_state_manager',\n 'core_message/message_drawer_router',\n 'core_message/message_drawer_routes',\n 'core/emoji/auto_complete',\n 'core/emoji/picker'\n],\nfunction(\n $,\n AutoRows,\n BackOffTimer,\n CustomEvents,\n Notification,\n Pending,\n PubSub,\n Str,\n Repository,\n MessageDrawerEvents,\n Constants,\n Patcher,\n Renderer,\n StateManager,\n MessageDrawerRouter,\n MessageDrawerRoutes,\n initialiseEmojiAutoComplete,\n initialiseEmojiPicker\n) {\n\n // Contains a cache of all view states that have been loaded so far\n // which saves us having to reload stuff with network requests when\n // switching between conversations.\n var stateCache = {};\n // The current data representation of the view.\n var viewState = null;\n var loadedAllMessages = false;\n var messagesOffset = 0;\n var newMessagesPollTimer = null;\n var isRendering = false;\n var renderBuffer = [];\n // If the UI is currently resetting.\n var isResetting = true;\n // If the UI is currently sending a message.\n var isSendingMessage = false;\n // If the UI is currently deleting a conversation.\n var isDeletingConversationContent = false;\n // A buffer of messages to send.\n var sendMessageBuffer = [];\n // These functions which will be generated when this module is\n // first called. See generateRenderFunction for details.\n var render = null;\n // The list of renderers that have been registered to render\n // this conversation. See generateRenderFunction for details.\n var renderers = [];\n\n var NEWEST_FIRST = Constants.NEWEST_MESSAGES_FIRST;\n var LOAD_MESSAGE_LIMIT = Constants.LOAD_MESSAGE_LIMIT;\n var MILLISECONDS_IN_SEC = Constants.MILLISECONDS_IN_SEC;\n var SELECTORS = Constants.SELECTORS;\n var CONVERSATION_TYPES = Constants.CONVERSATION_TYPES;\n\n /**\n * Get the other user userid.\n *\n * @return {Number} Userid.\n */\n var getOtherUserId = function() {\n if (!viewState || viewState.type == CONVERSATION_TYPES.PUBLIC) {\n return null;\n }\n\n var loggedInUserId = viewState.loggedInUserId;\n if (viewState.type == CONVERSATION_TYPES.SELF) {\n // It's a self-conversation, so the other user is the one logged in.\n return loggedInUserId;\n }\n\n var otherUserIds = Object.keys(viewState.members).filter(function(userId) {\n return loggedInUserId != userId;\n });\n\n return otherUserIds.length ? otherUserIds[0] : null;\n };\n\n /**\n * Search the cache to see if we've already loaded a private conversation\n * with the given user id.\n *\n * @param {Number} userId The id of the other user.\n * @return {Number|null} Conversation id.\n */\n var getCachedPrivateConversationIdFromUserId = function(userId) {\n return Object.keys(stateCache).reduce(function(carry, id) {\n if (!carry) {\n var state = stateCache[id].state;\n\n if (state.type != CONVERSATION_TYPES.PUBLIC) {\n if (userId in state.members) {\n // We've found a cached conversation for this user!\n carry = state.id;\n }\n }\n }\n\n return carry;\n }, null);\n };\n\n /**\n * Get profile info for logged in user.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object}\n */\n var getLoggedInUserProfile = function(body) {\n return {\n id: parseInt(body.attr('data-user-id'), 10),\n fullname: null,\n profileimageurl: null,\n profileimageurlsmall: null,\n isonline: null,\n showonlinestatus: null,\n isblocked: null,\n iscontact: null,\n isdeleted: null,\n canmessage: null,\n canmessageevenifblocked: null,\n requirescontact: null,\n contactrequests: [],\n cancreatecontact: null,\n };\n };\n\n /**\n * Get the messages offset value to load more messages.\n *\n * @return {Number}\n */\n var getMessagesOffset = function() {\n return messagesOffset;\n };\n\n /**\n * Set the messages offset value for loading more messages.\n *\n * @param {Number} value The offset value\n */\n var setMessagesOffset = function(value) {\n messagesOffset = value;\n stateCache[viewState.id].messagesOffset = value;\n };\n\n /**\n * Check if all messages have been loaded.\n *\n * @return {Bool}\n */\n var hasLoadedAllMessages = function() {\n return loadedAllMessages;\n };\n\n /**\n * Set whether all messages have been loaded or not.\n *\n * @param {Bool} value If all messages have been loaded.\n */\n var setLoadedAllMessages = function(value) {\n loadedAllMessages = value;\n stateCache[viewState.id].loadedAllMessages = value;\n };\n\n /**\n * Get the messages container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var getMessagesContainer = function(body) {\n return body.find(SELECTORS.MESSAGES_CONTAINER);\n };\n\n /**\n * Reformat the conversation for an event payload.\n *\n * @param {Object} state The view state.\n * @return {Object} New formatted conversation.\n */\n var formatConversationForEvent = function(state) {\n return {\n id: state.id,\n name: state.name,\n subname: state.subname,\n imageUrl: state.imageUrl,\n isFavourite: state.isFavourite,\n isMuted: state.isMuted,\n type: state.type,\n totalMemberCount: state.totalMemberCount,\n loggedInUserId: state.loggedInUserId,\n messages: state.messages.map(function(message) {\n return $.extend({}, message);\n }),\n members: Object.keys(state.members).map(function(id) {\n var formattedMember = $.extend({}, state.members[id]);\n formattedMember.contactrequests = state.members[id].contactrequests.map(function(request) {\n return $.extend({}, request);\n });\n return formattedMember;\n })\n };\n };\n\n /**\n * Load up an empty private conversation between the logged in user and the\n * other user. Sets all of the conversation details based on the other user.\n *\n * A conversation isn't created until the user sends the first message.\n *\n * @param {Object} loggedInUserProfile The logged in user profile.\n * @param {Number} otherUserId The other user id.\n * @return {Object} Profile returned from repository.\n */\n var loadEmptyPrivateConversation = function(loggedInUserProfile, otherUserId) {\n var loggedInUserId = loggedInUserProfile.id;\n // If the other user id is the same as the logged in user then this is a self\n // conversation.\n var conversationType = loggedInUserId == otherUserId ? CONVERSATION_TYPES.SELF : CONVERSATION_TYPES.PRIVATE;\n var newState = StateManager.setLoadingMembers(viewState, true);\n newState = StateManager.setLoadingMessages(newState, true);\n render(newState);\n\n return Repository.getMemberInfo(loggedInUserId, [otherUserId], true, true)\n .then(function(profiles) {\n if (profiles.length) {\n return profiles[0];\n } else {\n throw new Error('Unable to load other user profile');\n }\n })\n .then(function(profile) {\n // If the conversation is a self conversation then the profile loaded is the\n // logged in user so only add that to the members array.\n var members = conversationType == CONVERSATION_TYPES.SELF ? [profile] : [profile, loggedInUserProfile];\n var newState = StateManager.addMembers(viewState, members);\n newState = StateManager.setLoadingMembers(newState, false);\n newState = StateManager.setLoadingMessages(newState, false);\n newState = StateManager.setName(newState, profile.fullname);\n newState = StateManager.setType(newState, conversationType);\n newState = StateManager.setImageUrl(newState, profile.profileimageurl);\n newState = StateManager.setTotalMemberCount(newState, members.length);\n render(newState);\n return profile;\n })\n .catch(function(error) {\n var newState = StateManager.setLoadingMembers(viewState, false);\n render(newState);\n Notification.exception(error);\n });\n };\n\n /**\n * Create a new state from a conversation object.\n *\n * @param {Object} conversation The conversation object.\n * @param {Number} loggedInUserId The logged in user id.\n * @return {Object} new state.\n */\n var updateStateFromConversation = function(conversation, loggedInUserId) {\n var otherUser = null;\n if (conversation.type == CONVERSATION_TYPES.PRIVATE) {\n // For private conversations, remove current logged in user from the members list to get the other user.\n var otherUsers = conversation.members.filter(function(member) {\n return member.id != loggedInUserId;\n });\n otherUser = otherUsers.length ? otherUsers[0] : null;\n } else if (conversation.type == CONVERSATION_TYPES.SELF) {\n // Self-conversations have only one member.\n otherUser = conversation.members[0];\n }\n\n var name = conversation.name;\n var imageUrl = conversation.imageurl;\n\n if (conversation.type != CONVERSATION_TYPES.PUBLIC) {\n name = name || otherUser ? otherUser.fullname : '';\n imageUrl = imageUrl || otherUser ? otherUser.profileimageurl : '';\n }\n\n var newState = StateManager.addMembers(viewState, conversation.members);\n newState = StateManager.setName(newState, name);\n newState = StateManager.setSubname(newState, conversation.subname);\n newState = StateManager.setType(newState, conversation.type);\n newState = StateManager.setImageUrl(newState, imageUrl);\n newState = StateManager.setTotalMemberCount(newState, conversation.membercount);\n newState = StateManager.setIsFavourite(newState, conversation.isfavourite);\n newState = StateManager.setIsMuted(newState, conversation.ismuted);\n newState = StateManager.addMessages(newState, conversation.messages);\n newState = StateManager.setCanDeleteMessagesForAllUsers(newState, conversation.candeletemessagesforallusers);\n return newState;\n };\n\n /**\n * Get the details for a conversation from the conversation id.\n *\n * @param {Number} conversationId The conversation id.\n * @param {Object} loggedInUserProfile The logged in user profile.\n * @param {Number} messageLimit The number of messages to include.\n * @param {Number} messageOffset The number of messages to skip.\n * @param {Bool} newestFirst Order messages newest first.\n * @return {Object} Promise resolved when loaded.\n */\n var loadNewConversation = function(\n conversationId,\n loggedInUserProfile,\n messageLimit,\n messageOffset,\n newestFirst\n ) {\n var loggedInUserId = loggedInUserProfile.id;\n var newState = StateManager.setLoadingMembers(viewState, true);\n newState = StateManager.setLoadingMessages(newState, true);\n render(newState);\n\n return Repository.getConversation(\n loggedInUserId,\n conversationId,\n true,\n true,\n 0,\n 0,\n messageLimit + 1,\n messageOffset,\n newestFirst\n )\n .then(function(conversation) {\n if (conversation.messages.length > messageLimit) {\n conversation.messages = conversation.messages.slice(1);\n } else {\n setLoadedAllMessages(true);\n }\n\n setMessagesOffset(messageOffset + messageLimit);\n\n return conversation;\n })\n .then(function(conversation) {\n var hasLoggedInUser = conversation.members.filter(function(member) {\n return member.id == loggedInUserProfile.id;\n });\n\n if (hasLoggedInUser.length < 1) {\n conversation.members = conversation.members.concat([loggedInUserProfile]);\n }\n\n var newState = updateStateFromConversation(conversation, loggedInUserProfile.id);\n newState = StateManager.setLoadingMembers(newState, false);\n newState = StateManager.setLoadingMessages(newState, false);\n return render(newState)\n .then(function() {\n return conversation;\n });\n })\n .then(function() {\n return markConversationAsRead(conversationId);\n })\n .catch(function(error) {\n var newState = StateManager.setLoadingMembers(viewState, false);\n newState = StateManager.setLoadingMessages(newState, false);\n render(newState);\n Notification.exception(error);\n });\n };\n\n /**\n * Get the details for a conversation from and existing conversation object.\n *\n * @param {Object} conversation The conversation object.\n * @param {Object} loggedInUserProfile The logged in user profile.\n * @param {Number} messageLimit The number of messages to include.\n * @param {Bool} newestFirst Order messages newest first.\n * @return {Object} Promise resolved when loaded.\n */\n var loadExistingConversation = function(\n conversation,\n loggedInUserProfile,\n messageLimit,\n newestFirst\n ) {\n var hasLoggedInUser = conversation.members.filter(function(member) {\n return member.id == loggedInUserProfile.id;\n });\n\n if (hasLoggedInUser.length < 1) {\n conversation.members = conversation.members.concat([loggedInUserProfile]);\n }\n\n var messageCount = conversation.messages.length;\n var hasLoadedEnoughMessages = messageCount >= messageLimit;\n var newState = updateStateFromConversation(conversation, loggedInUserProfile.id);\n newState = StateManager.setLoadingMembers(newState, false);\n newState = StateManager.setLoadingMessages(newState, !hasLoadedEnoughMessages);\n var renderPromise = render(newState);\n\n return renderPromise.then(function() {\n if (!hasLoadedEnoughMessages) {\n // We haven't got enough messages so let's load some more.\n return loadMessages(conversation.id, messageLimit, messageCount, newestFirst, []);\n } else {\n // We've got enough messages. No need to load any more for now.\n return {messages: conversation.messages};\n }\n })\n .then(function() {\n var messages = viewState.messages;\n // Update the offset to reflect the number of messages we've loaded.\n setMessagesOffset(messages.length);\n markConversationAsRead(viewState.id);\n\n return messages;\n })\n .catch(Notification.exception);\n };\n\n /**\n * Load messages for this conversation and pass them to the renderer.\n *\n * @param {Number} conversationId Conversation id.\n * @param {Number} limit Number of messages to load.\n * @param {Number} offset Get messages from offset.\n * @param {Bool} newestFirst Get newest messages first.\n * @param {Array} ignoreList Ignore any messages with ids in this list.\n * @param {Number|null} timeFrom Only get messages from this time onwards.\n * @return {Promise} renderer promise.\n */\n var loadMessages = function(conversationId, limit, offset, newestFirst, ignoreList, timeFrom) {\n return Repository.getMessages(\n viewState.loggedInUserId,\n conversationId,\n limit ? limit + 1 : limit,\n offset,\n newestFirst,\n timeFrom\n )\n .then(function(result) {\n // Prevent older requests from contaminating the current view.\n if (result.id != viewState.id) {\n result.messages = [];\n // Purge old conversation cache to prevent messages lose.\n if (result.id in stateCache) {\n delete stateCache[result.id];\n }\n }\n\n return result;\n })\n .then(function(result) {\n if (result.messages.length && ignoreList.length) {\n result.messages = result.messages.filter(function(message) {\n // Skip any messages in our ignore list.\n return ignoreList.indexOf(parseInt(message.id, 10)) < 0;\n });\n }\n\n return result;\n })\n .then(function(result) {\n if (!limit) {\n return result;\n } else if (result.messages.length > limit) {\n // Ignore the last result which was just to test if there are more\n // to load.\n result.messages = result.messages.slice(0, -1);\n } else {\n setLoadedAllMessages(true);\n }\n\n return result;\n })\n .then(function(result) {\n var membersToAdd = result.members.filter(function(member) {\n return !(member.id in viewState.members);\n });\n var newState = StateManager.addMembers(viewState, membersToAdd);\n newState = StateManager.addMessages(newState, result.messages);\n newState = StateManager.setLoadingMessages(newState, false);\n return render(newState)\n .then(function() {\n return result;\n });\n })\n .catch(function(error) {\n var newState = StateManager.setLoadingMessages(viewState, false);\n render(newState);\n // Re-throw the error for other error handlers.\n throw error;\n });\n };\n\n /**\n * Create a callback function for getting new messages for this conversation.\n *\n * @param {Number} conversationId Conversation id.\n * @param {Bool} newestFirst Show newest messages first\n * @return {Function} Callback function that returns a renderer promise.\n */\n var getLoadNewMessagesCallback = function(conversationId, newestFirst) {\n return function() {\n var messages = viewState.messages;\n var mostRecentMessage = messages.length ? messages[messages.length - 1] : null;\n var lastTimeCreated = mostRecentMessage ? mostRecentMessage.timeCreated : null;\n\n if (lastTimeCreated && !isResetting && !isSendingMessage && !isDeletingConversationContent) {\n // There may be multiple messages with the same time created value since\n // the accuracy is only down to the second. The server will include these\n // messages in the result (since it does a >= comparison on time from) so\n // we need to filter them back out of the result so that we're left only\n // with the new messages.\n var ignoreMessageIds = [];\n for (var i = messages.length - 1; i >= 0; i--) {\n var message = messages[i];\n if (message.timeCreated === lastTimeCreated) {\n ignoreMessageIds.push(message.id);\n } else {\n // Since the messages are ordered in ascending order of time created\n // we can break as soon as we hit a message with a different time created\n // because we know all other messages will have lower values.\n break;\n }\n }\n\n return loadMessages(\n conversationId,\n 0,\n 0,\n newestFirst,\n ignoreMessageIds,\n lastTimeCreated\n )\n .then(function(result) {\n if (result.messages.length) {\n // If we found some results then restart the polling timer\n // because the other user might be sending messages.\n newMessagesPollTimer.restart();\n // We've also got a new last message so publish that for other\n // components to update.\n var conversation = formatConversationForEvent(viewState);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation);\n return markConversationAsRead(conversationId);\n } else {\n return result;\n }\n });\n }\n\n return $.Deferred().resolve().promise();\n };\n };\n\n /**\n * Mark a conversation as read.\n *\n * @param {Number} conversationId The conversation id.\n * @return {Promise} The renderer promise.\n */\n var markConversationAsRead = function(conversationId) {\n var loggedInUserId = viewState.loggedInUserId;\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:markConversationAsRead');\n\n return Repository.markAllConversationMessagesAsRead(loggedInUserId, conversationId)\n .then(function() {\n var newState = StateManager.markMessagesAsRead(viewState, viewState.messages);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_READ, conversationId);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager there is request to block a user and run the renderer\n * to show the block user dialogue.\n *\n * @param {Number} userId User id.\n */\n var requestBlockUser = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.addPendingBlockUsersById(viewState, [userId]);\n render(newState);\n };\n\n /**\n * Send the repository a request to block a user, update the statemanager and publish\n * a contact has been blocked.\n *\n * @param {Number} userId User id of user to block.\n * @return {Promise} Renderer promise.\n */\n var blockUser = function(userId) {\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:blockUser');\n\n render(newState);\n\n return Repository.blockUser(viewState.loggedInUserId, userId)\n .then(function(profile) {\n var newState = StateManager.addMembers(viewState, [profile]);\n newState = StateManager.removePendingBlockUsersById(newState, [userId]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n PubSub.publish(MessageDrawerEvents.CONTACT_BLOCKED, userId);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager there is a request to unblock a user and run the renderer\n * to show the unblock user dialogue.\n *\n * @param {Number} userId User id of user to unblock.\n */\n var requestUnblockUser = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.addPendingUnblockUsersById(viewState, [userId]);\n render(newState);\n };\n\n /**\n * Send the repository a request to unblock a user, update the statemanager and publish\n * a contact has been unblocked.\n *\n * @param {Number} userId User id of user to unblock.\n * @return {Promise} Renderer promise.\n */\n var unblockUser = function(userId) {\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:unblockUser');\n render(newState);\n\n return Repository.unblockUser(viewState.loggedInUserId, userId)\n .then(function(profile) {\n var newState = StateManager.addMembers(viewState, [profile]);\n newState = StateManager.removePendingUnblockUsersById(newState, [userId]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n PubSub.publish(MessageDrawerEvents.CONTACT_UNBLOCKED, userId);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager there is a request to remove a user from the contact list\n * and run the renderer to show the remove user from contacts dialogue.\n *\n * @param {Number} userId User id of user to remove from contacts.\n */\n var requestRemoveContact = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.addPendingRemoveContactsById(viewState, [userId]);\n render(newState);\n };\n\n /**\n * Send the repository a request to remove a user from the contacts list. update the statemanager\n * and publish a contact has been removed.\n *\n * @param {Number} userId User id of user to remove from contacts.\n * @return {Promise} Renderer promise.\n */\n var removeContact = function(userId) {\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:removeContact');\n render(newState);\n\n return Repository.deleteContacts(viewState.loggedInUserId, [userId])\n .then(function(profiles) {\n var newState = StateManager.addMembers(viewState, profiles);\n newState = StateManager.removePendingRemoveContactsById(newState, [userId]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n PubSub.publish(MessageDrawerEvents.CONTACT_REMOVED, userId);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager there is a request to add a user to the contact list\n * and run the renderer to show the add user to contacts dialogue.\n *\n * @param {Number} userId User id of user to add to contacts.\n */\n var requestAddContact = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.addPendingAddContactsById(viewState, [userId]);\n render(newState);\n };\n\n /**\n * Send the repository a request to add a user to the contacts list. update the statemanager\n * and publish a contact has been added.\n *\n * @param {Number} userId User id of user to add to contacts.\n * @return {Promise} Renderer promise.\n */\n var addContact = function(userId) {\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:addContactRequests');\n render(newState);\n\n return Repository.createContactRequest(viewState.loggedInUserId, userId)\n .then(function(response) {\n if (!response.request) {\n throw new Error(response.warnings[0].message);\n }\n\n return response.request;\n })\n .then(function(request) {\n var newState = StateManager.removePendingAddContactsById(viewState, [userId]);\n newState = StateManager.addContactRequests(newState, [request]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Set the current conversation as a favourite conversation.\n *\n * @return {Promise} Renderer promise.\n */\n var setFavourite = function() {\n var userId = viewState.loggedInUserId;\n var conversationId = viewState.id;\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:setFavourite');\n\n return Repository.setFavouriteConversations(userId, [conversationId])\n .then(function() {\n var newState = StateManager.setIsFavourite(viewState, true);\n return render(newState);\n })\n .then(function() {\n return PubSub.publish(\n MessageDrawerEvents.CONVERSATION_SET_FAVOURITE,\n formatConversationForEvent(viewState)\n );\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Unset the current conversation as a favourite conversation.\n *\n * @return {Promise} Renderer promise.\n */\n var unsetFavourite = function() {\n var userId = viewState.loggedInUserId;\n var conversationId = viewState.id;\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:unsetFavourite');\n\n return Repository.unsetFavouriteConversations(userId, [conversationId])\n .then(function() {\n var newState = StateManager.setIsFavourite(viewState, false);\n return render(newState);\n })\n .then(function() {\n return PubSub.publish(\n MessageDrawerEvents.CONVERSATION_UNSET_FAVOURITE,\n formatConversationForEvent(viewState)\n );\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Set the current conversation as a muted conversation.\n *\n * @return {Promise} Renderer promise.\n */\n var setMuted = function() {\n var userId = viewState.loggedInUserId;\n var conversationId = viewState.id;\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:markConversationAsRead');\n\n return Repository.setMutedConversations(userId, [conversationId])\n .then(function() {\n var newState = StateManager.setIsMuted(viewState, true);\n return render(newState);\n })\n .then(function() {\n return PubSub.publish(\n MessageDrawerEvents.CONVERSATION_SET_MUTED,\n formatConversationForEvent(viewState)\n );\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Unset the current conversation as a muted conversation.\n *\n * @return {Promise} Renderer promise.\n */\n var unsetMuted = function() {\n var userId = viewState.loggedInUserId;\n var conversationId = viewState.id;\n\n return Repository.unsetMutedConversations(userId, [conversationId])\n .then(function() {\n var newState = StateManager.setIsMuted(viewState, false);\n return render(newState);\n })\n .then(function() {\n return PubSub.publish(\n MessageDrawerEvents.CONVERSATION_UNSET_MUTED,\n formatConversationForEvent(viewState)\n );\n });\n };\n\n /**\n * Tell the statemanager there is a request to delete the selected messages\n * and run the renderer to show confirm delete messages dialogue.\n *\n * @param {Number} userId User id.\n */\n var requestDeleteSelectedMessages = function(userId) {\n var selectedMessageIds = viewState.selectedMessageIds;\n cancelRequest(userId);\n var newState = StateManager.addPendingDeleteMessagesById(viewState, selectedMessageIds);\n render(newState);\n };\n\n /**\n * Send the repository a request to delete the messages pending deletion. Update the statemanager\n * and publish a message deletion event.\n *\n * @return {Promise} Renderer promise.\n */\n var deleteSelectedMessages = function() {\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:deleteSelectedMessages');\n var messageIds = viewState.pendingDeleteMessageIds;\n var sentMessages = viewState.messages.filter(function(message) {\n // If a message sendState is null then it means it was loaded from the server or if it's\n // set to sent then it means the user has successfully sent it in this page load.\n return messageIds.indexOf(message.id) >= 0 && (message.sendState == 'sent' || message.sendState === null);\n });\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n\n render(newState);\n\n var deleteMessagesPromise = $.Deferred().resolve().promise();\n\n\n if (sentMessages.length) {\n // We only need to send a request to the server if we're trying to delete messages that\n // have successfully been sent.\n var sentMessageIds = sentMessages.map(function(message) {\n return message.id;\n });\n if (newState.deleteMessagesForAllUsers) {\n deleteMessagesPromise = Repository.deleteMessagesForAllUsers(viewState.loggedInUserId, sentMessageIds);\n } else {\n deleteMessagesPromise = Repository.deleteMessages(viewState.loggedInUserId, sentMessageIds);\n }\n }\n\n // Mark that we are deleting content from the conversation to prevent updates of it.\n isDeletingConversationContent = true;\n\n // Stop polling for new messages to the open conversation.\n if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\n }\n\n return deleteMessagesPromise.then(function() {\n var newState = StateManager.removeMessagesById(viewState, messageIds);\n newState = StateManager.removePendingDeleteMessagesById(newState, messageIds);\n newState = StateManager.removeSelectedMessagesById(newState, messageIds);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n newState = StateManager.setDeleteMessagesForAllUsers(newState, false);\n\n var prevLastMessage = viewState.messages[viewState.messages.length - 1];\n var newLastMessage = newState.messages.length ? newState.messages[newState.messages.length - 1] : null;\n\n if (newLastMessage && newLastMessage.id != prevLastMessage.id) {\n var conversation = formatConversationForEvent(newState);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation);\n } else if (!newState.messages.length) {\n PubSub.publish(MessageDrawerEvents.CONVERSATION_DELETED, newState.id);\n }\n\n isDeletingConversationContent = false;\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(Notification.exception);\n };\n\n /**\n * Tell the statemanager there is a request to delete a conversation\n * and run the renderer to show confirm delete conversation dialogue.\n *\n * @param {Number} userId User id of other user.\n */\n var requestDeleteConversation = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.setPendingDeleteConversation(viewState, true);\n render(newState);\n };\n\n /**\n * Send the repository a request to delete a conversation. Update the statemanager\n * and publish a conversation deleted event.\n *\n * @return {Promise} Renderer promise.\n */\n var deleteConversation = function() {\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:markConversationAsRead');\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n render(newState);\n\n // Mark that we are deleting the conversation to prevent updates of it.\n isDeletingConversationContent = true;\n\n // Stop polling for new messages to the open conversation.\n if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\n }\n\n return Repository.deleteConversation(viewState.loggedInUserId, viewState.id)\n .then(function() {\n var newState = StateManager.removeMessages(viewState, viewState.messages);\n newState = StateManager.removeSelectedMessagesById(newState, viewState.selectedMessageIds);\n newState = StateManager.setPendingDeleteConversation(newState, false);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_DELETED, newState.id);\n\n isDeletingConversationContent = false;\n\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager to cancel all pending actions.\n *\n * @param {Number} userId User id.\n */\n var cancelRequest = function(userId) {\n var pendingDeleteMessageIds = viewState.pendingDeleteMessageIds;\n var newState = StateManager.removePendingAddContactsById(viewState, [userId]);\n newState = StateManager.removePendingRemoveContactsById(newState, [userId]);\n newState = StateManager.removePendingUnblockUsersById(newState, [userId]);\n newState = StateManager.removePendingBlockUsersById(newState, [userId]);\n newState = StateManager.removePendingDeleteMessagesById(newState, pendingDeleteMessageIds);\n newState = StateManager.setPendingDeleteConversation(newState, false);\n newState = StateManager.setDeleteMessagesForAllUsers(newState, false);\n render(newState);\n };\n\n /**\n * Accept the contact request from the given user.\n *\n * @param {Number} userId User id of other user.\n * @return {Promise} Renderer promise.\n */\n var acceptContactRequest = function(userId) {\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:acceptContactRequest');\n\n // Search the list of the logged in user's contact requests to find the\n // one from this user.\n var loggedInUserId = viewState.loggedInUserId;\n var requests = viewState.members[userId].contactrequests.filter(function(request) {\n return request.requesteduserid == loggedInUserId;\n });\n var request = requests[0];\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n render(newState);\n\n return Repository.acceptContactRequest(userId, loggedInUserId)\n .then(function(profile) {\n var newState = StateManager.removeContactRequests(viewState, [request]);\n newState = StateManager.addMembers(viewState, [profile]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n return render(newState);\n })\n .then(function() {\n PubSub.publish(MessageDrawerEvents.CONTACT_ADDED, viewState.members[userId]);\n PubSub.publish(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED, request);\n return;\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Decline the contact request from the given user.\n *\n * @param {Number} userId User id of other user.\n * @return {Promise} Renderer promise.\n */\n var declineContactRequest = function(userId) {\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:declineContactRequest');\n\n // Search the list of the logged in user's contact requests to find the\n // one from this user.\n var loggedInUserId = viewState.loggedInUserId;\n var requests = viewState.members[userId].contactrequests.filter(function(request) {\n return request.requesteduserid == loggedInUserId;\n });\n var request = requests[0];\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n render(newState);\n\n return Repository.declineContactRequest(userId, loggedInUserId)\n .then(function(profile) {\n var newState = StateManager.removeContactRequests(viewState, [request]);\n newState = StateManager.addMembers(viewState, [profile]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n return render(newState);\n })\n .then(function() {\n PubSub.publish(MessageDrawerEvents.CONTACT_REQUEST_DECLINED, request);\n return;\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Send all of the messages in the buffer to the server to be created. Update the\n * UI with the newly created message information.\n *\n * This function will recursively call itself in order to make sure the buffer is\n * always being processed.\n */\n var processSendMessageBuffer = function() {\n if (isSendingMessage) {\n // We're already sending messages so nothing to do.\n return;\n }\n if (!sendMessageBuffer.length) {\n // No messages waiting to send. Nothing to do.\n return;\n }\n\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:processSendMessageBuffer');\n\n // Flag that we're processing the queue.\n isSendingMessage = true;\n // Grab all of the messages in the buffer.\n var messagesToSend = sendMessageBuffer.slice();\n // Empty the buffer since we're processing it.\n sendMessageBuffer = [];\n var conversationId = viewState.id;\n var newConversationId = null;\n var messagesText = messagesToSend.map(function(message) {\n return message.text;\n });\n var messageIds = messagesToSend.map(function(message) {\n return message.id;\n });\n var sendMessagePromise = null;\n var newCanDeleteMessagesForAllUsers = null;\n if (!conversationId && (viewState.type != CONVERSATION_TYPES.PUBLIC)) {\n // If it's a new private conversation then we need to use the old\n // web service function to create the conversation.\n var otherUserId = getOtherUserId();\n sendMessagePromise = Repository.sendMessagesToUser(otherUserId, messagesText)\n .then(function(messages) {\n if (messages.length) {\n newConversationId = parseInt(messages[0].conversationid, 10);\n newCanDeleteMessagesForAllUsers = messages[0].candeletemessagesforallusers;\n }\n return messages;\n });\n } else {\n sendMessagePromise = Repository.sendMessagesToConversation(conversationId, messagesText);\n }\n\n sendMessagePromise\n .then(function(messages) {\n var newMessageIds = messages.map(function(message) {\n return message.id;\n });\n var data = [];\n var selectedToRemove = [];\n var selectedToAdd = [];\n\n messagesToSend.forEach(function(oldMessage, index) {\n var newMessage = messages[index];\n // Update messages expects and array of arrays where the first value\n // is the old message to update and the second value is the new values\n // to set.\n data.push([oldMessage, newMessage]);\n\n if (viewState.selectedMessageIds.indexOf(oldMessage.id) >= 0) {\n // If the message was added to the \"selected messages\" list while it was still\n // being sent then we should update it's id in that list now to make sure future\n // actions work.\n selectedToRemove.push(oldMessage.id);\n selectedToAdd.push(newMessage.id);\n }\n });\n var newState = StateManager.updateMessages(viewState, data);\n newState = StateManager.setMessagesSendSuccessById(newState, newMessageIds);\n\n if (selectedToRemove.length) {\n newState = StateManager.removeSelectedMessagesById(newState, selectedToRemove);\n }\n\n if (selectedToAdd.length) {\n newState = StateManager.addSelectedMessagesById(newState, selectedToAdd);\n }\n\n var conversation = formatConversationForEvent(newState);\n\n if (!newState.id) {\n // If this message created the conversation then save the conversation\n // id.\n newState = StateManager.setId(newState, newConversationId);\n conversation.id = newConversationId;\n resetMessagePollTimer(newConversationId);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_CREATED, conversation);\n newState = StateManager.setCanDeleteMessagesForAllUsers(newState, newCanDeleteMessagesForAllUsers);\n }\n\n // Update the UI with the new message values from the server.\n render(newState);\n // Recurse just in case there has been more messages added to the buffer.\n isSendingMessage = false;\n processSendMessageBuffer();\n PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation);\n return;\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(function(e) {\n var errorMessage;\n if (e.message) {\n errorMessage = $.Deferred().resolve(e.message).promise();\n } else {\n errorMessage = Str.get_string('unknownerror', 'core');\n }\n\n var handleFailedMessages = function(errorMessage) {\n // We failed to create messages so remove the old messages from the pending queue\n // and update the UI to indicate that the message failed.\n var newState = StateManager.setMessagesSendFailById(viewState, messageIds, errorMessage);\n render(newState);\n isSendingMessage = false;\n processSendMessageBuffer();\n };\n\n errorMessage.then(handleFailedMessages)\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(function(e) {\n // Hrmm, we can't even load the error messages string! We'll have to\n // hard code something in English here if we still haven't got a message\n // to show.\n var finalError = e.message || 'Something went wrong!';\n handleFailedMessages(finalError);\n });\n });\n };\n\n /**\n * Create a plain version of an HTML text.\n *\n * This texts is used as a message preview while is sent to the server. This way\n * it is possible to prevent self-xss.\n *\n * @param {String} text Text to send.\n * @return {String} The plain text version of the text.\n */\n const previewText = function(text) {\n // Remove all script and styles from text (we don't want it there).\n let plaintext = text.replace(//gi, '');\n plaintext = plaintext.replace(//gi, '');\n // Beautify a bit the output adding some line breaks.\n plaintext = plaintext.replace(/<\\/div>/ig, '\\n');\n plaintext = plaintext.replace(/<\\/li>/ig, '\\n');\n plaintext = plaintext.replace(/
  • /ig, ' * ');\n plaintext = plaintext.replace(/<\\/ul>/ig, '\\n');\n plaintext = plaintext.replace(/<\\/p>/ig, '\\n');\n plaintext = plaintext.replace(/]*>/gi, '\\n');\n // Remove all remaining tags and convert line breaks into html.\n plaintext = plaintext.replace(/<[^>]+>/ig, '');\n plaintext = plaintext.replace(/\\n+/ig, '\\n');\n return plaintext.replace(/\\n/ig, '
    ');\n };\n\n /**\n * Buffers messages to be sent to the server. We use a buffer here to allow the\n * user to freely input messages without blocking the interface for them.\n *\n * Instead we just queue all of their messages up and send them as fast as we can.\n *\n * @param {String} text Text to send.\n */\n var sendMessage = function(text) {\n var id = 'temp' + Date.now();\n // Render a preview version of the message while sending.\n let loadingmessage = {\n id: id,\n useridfrom: viewState.loggedInUserId,\n text: previewText(text),\n timecreated: null\n };\n var newState = StateManager.addMessages(viewState, [loadingmessage]);\n render(newState);\n // Send the real message.\n var message = {\n id: id,\n useridfrom: viewState.loggedInUserId,\n text: text,\n timecreated: null\n };\n sendMessageBuffer.push(message);\n processSendMessageBuffer();\n\n // Remove the unsent message attribute so we can be notified of new unsent messages (see storeUnsentMessage).\n const textArea = document.querySelector(SELECTORS.MESSAGE_TEXT_AREA);\n if (textArea) {\n textArea.removeAttribute('data-unsent-message-viewed');\n }\n };\n\n /**\n * Retry sending a message that failed.\n *\n * @param {Object} message The message to send.\n */\n var retrySendMessage = function(message) {\n var newState = StateManager.setMessagesSendPendingById(viewState, [message.id]);\n render(newState);\n sendMessageBuffer.push(message);\n processSendMessageBuffer();\n };\n\n /**\n * Toggle the selected messages update the statemanager and render the result.\n *\n * @param {Number} messageId The id of the message to be toggled\n */\n var toggleSelectMessage = function(messageId) {\n var newState = viewState;\n\n if (viewState.selectedMessageIds.indexOf(messageId) > -1) {\n newState = StateManager.removeSelectedMessagesById(viewState, [messageId]);\n } else {\n newState = StateManager.addSelectedMessagesById(viewState, [messageId]);\n }\n\n render(newState);\n };\n\n /**\n * Cancel edit mode (selecting the messages).\n */\n var cancelEditMode = function() {\n cancelRequest(getOtherUserId());\n var newState = StateManager.removeSelectedMessagesById(viewState, viewState.selectedMessageIds);\n render(newState);\n };\n\n /**\n * Process the patches in the render buffer one at a time in order until the\n * buffer is empty.\n *\n * @param {Object} header The conversation header container element.\n * @param {Object} body The conversation body container element.\n * @param {Object} footer The conversation footer container element.\n */\n var processRenderBuffer = function(header, body, footer) {\n if (isRendering) {\n return;\n }\n\n if (!renderBuffer.length) {\n return;\n }\n\n isRendering = true;\n var renderable = renderBuffer.shift();\n var renderPromises = renderers.map(function(renderFunc) {\n return renderFunc(renderable.patch);\n });\n\n $.when.apply(null, renderPromises)\n .then(function() {\n isRendering = false;\n renderable.deferred.resolve(true);\n // Keep processing the buffer until it's empty.\n processRenderBuffer(header, body, footer);\n\n return;\n })\n .catch(function(error) {\n isRendering = false;\n renderable.deferred.reject(error);\n Notification.exception(error);\n });\n };\n\n /**\n * Create a function to render the Conversation.\n *\n * @param {Object} header The conversation header container element.\n * @param {Object} body The conversation body container element.\n * @param {Object} footer The conversation footer container element.\n * @param {Bool} isNewConversation Has someone else already initialised a conversation?\n * @return {Promise} Renderer promise.\n */\n var generateRenderFunction = function(header, body, footer, isNewConversation) {\n var rendererFunc = function(patch) {\n return Renderer.render(header, body, footer, patch);\n };\n\n if (!isNewConversation) {\n // Looks like someone got here before us! We'd better update our\n // UI to make sure it matches.\n var initialState = StateManager.buildInitialState(viewState.midnight, viewState.loggedInUserId, viewState.id);\n var syncPatch = Patcher.buildPatch(initialState, viewState);\n rendererFunc(syncPatch);\n }\n\n renderers.push(rendererFunc);\n\n return function(newState) {\n var patch = Patcher.buildPatch(viewState, newState);\n var deferred = $.Deferred();\n\n // Check if the patch has any data. Ignore empty patches.\n if (Object.keys(patch).length) {\n // Add the patch to the render buffer which gets processed in order.\n renderBuffer.push({\n patch: patch,\n deferred: deferred\n });\n } else {\n deferred.resolve(true);\n }\n // This is a great place to add in some console logging if you need\n // to debug something. You can log the current state, the next state,\n // and the generated patch and see exactly what will be updated.\n\n // Optimistically update the state. We're going to assume that the rendering\n // will always succeed. The rendering is asynchronous (annoyingly) so it's buffered\n // but it'll reach eventual consistency with the current state.\n viewState = newState;\n if (newState.id) {\n // Only cache created conversations.\n stateCache[newState.id] = {\n state: newState,\n messagesOffset: getMessagesOffset(),\n loadedAllMessages: hasLoadedAllMessages()\n };\n }\n\n // Start processing the buffer.\n processRenderBuffer(header, body, footer);\n\n return deferred.promise();\n };\n };\n\n /**\n * Create a confirm action function.\n *\n * @param {Function} actionCallback The callback function.\n * @return {Function} Confirm action handler.\n */\n var generateConfirmActionHandler = function(actionCallback) {\n return function(e, data) {\n if (!viewState.loadingConfirmAction) {\n actionCallback(getOtherUserId());\n var newState = StateManager.setLoadingConfirmAction(viewState, false);\n render(newState);\n }\n data.originalEvent.preventDefault();\n };\n };\n\n /**\n * Send message event handler.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleSendMessage = function(e, data) {\n var target = $(e.target);\n var footerContainer = target.closest(SELECTORS.FOOTER_CONTAINER);\n var textArea = footerContainer.find(SELECTORS.MESSAGE_TEXT_AREA);\n var text = textArea.val().trim();\n\n if (text !== '') {\n sendMessage(text);\n textArea.val('');\n textArea.focus();\n }\n\n data.originalEvent.preventDefault();\n };\n\n /**\n * Select message event handler.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleSelectMessage = function(e, data) {\n var selection = window.getSelection();\n var target = $(e.target);\n\n if (selection.toString() != '') {\n // Bail if we're selecting.\n return;\n }\n\n if (target.is('a')) {\n // Clicking on a link in the message so ignore it.\n return;\n }\n\n var element = target.closest(SELECTORS.MESSAGE);\n var messageId = element.attr('data-message-id');\n\n toggleSelectMessage(messageId);\n\n data.originalEvent.preventDefault();\n };\n\n /**\n * Handle retry sending of message.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleRetrySendMessage = function(e, data) {\n var target = $(e.target);\n var element = target.closest(SELECTORS.MESSAGE);\n var messageId = element.attr('data-message-id');\n var messages = viewState.messages.filter(function(message) {\n return message.id == messageId;\n });\n var message = messages.length ? messages[0] : null;\n\n if (message) {\n retrySendMessage(message);\n }\n\n data.originalEvent.preventDefault();\n data.originalEvent.stopPropagation();\n e.stopPropagation();\n };\n\n /**\n * Cancel edit mode event handler.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleCancelEditMode = function(e, data) {\n cancelEditMode();\n data.originalEvent.preventDefault();\n };\n\n /**\n * Show the view contact page.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @return {Function} View contact handler.\n */\n var generateHandleViewContact = function(namespace) {\n return function(e, data) {\n var otherUserId = getOtherUserId();\n var otherUser = viewState.members[otherUserId];\n MessageDrawerRouter.go(namespace, MessageDrawerRoutes.VIEW_CONTACT, otherUser);\n data.originalEvent.preventDefault();\n };\n };\n\n /**\n * Set this conversation as a favourite.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleSetFavourite = function(e, data) {\n setFavourite().catch(Notification.exception);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Unset this conversation as a favourite.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleUnsetFavourite = function(e, data) {\n unsetFavourite().catch(Notification.exception);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Show the view group info page.\n * Set this conversation as muted.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleSetMuted = function(e, data) {\n setMuted().catch(Notification.exception);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Unset this conversation as muted.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleUnsetMuted = function(e, data) {\n unsetMuted().catch(Notification.exception);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Handle clicking on the checkbox that toggles deleting messages for\n * all users.\n *\n * @param {Object} e Element this event handler is called on.\n */\n var handleDeleteMessagesForAllUsersToggle = function(e) {\n var newValue = $(e.target).prop('checked');\n var newState = StateManager.setDeleteMessagesForAllUsers(viewState, newValue);\n render(newState);\n };\n\n /**\n * Show the view contact page.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @return {Function} View group info handler.\n */\n var generateHandleViewGroupInfo = function(namespace) {\n return function(e, data) {\n MessageDrawerRouter.go(\n namespace,\n MessageDrawerRoutes.VIEW_GROUP_INFO,\n {\n id: viewState.id,\n name: viewState.name,\n subname: viewState.subname,\n imageUrl: viewState.imageUrl,\n totalMemberCount: viewState.totalMemberCount\n },\n viewState.loggedInUserId\n );\n data.originalEvent.preventDefault();\n };\n };\n\n /**\n * Handle clicking on the emoji toggle button.\n *\n * @param {Object} e The event\n * @param {Object} data The custom interaction event data\n */\n var handleToggleEmojiPicker = function(e, data) {\n var newState = StateManager.setShowEmojiPicker(viewState, !viewState.showEmojiPicker);\n render(newState);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Handle clicking outside the emoji picker to close it.\n *\n * @param {Object} e The event\n */\n var handleCloseEmojiPicker = function(e) {\n var target = $(e.target);\n\n if (\n viewState.showEmojiPicker &&\n !target.closest(SELECTORS.EMOJI_PICKER_CONTAINER).length &&\n !target.closest(SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON).length\n ) {\n var newState = StateManager.setShowEmojiPicker(viewState, false);\n render(newState);\n }\n };\n\n /**\n * Listen to, and handle events for conversations.\n *\n * @param {string} namespace The route namespace.\n * @param {Object} header Conversation header container element.\n * @param {Object} body Conversation body container element.\n * @param {Object} footer Conversation footer container element.\n */\n var registerEventListeners = function(namespace, header, body, footer) {\n var isLoadingMoreMessages = false;\n var messagesContainer = getMessagesContainer(body);\n var emojiPickerElement = footer.find(SELECTORS.EMOJI_PICKER);\n var emojiAutoCompleteContainer = footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER);\n var messageTextArea = footer.find(SELECTORS.MESSAGE_TEXT_AREA);\n var headerActivateHandlers = [\n [SELECTORS.ACTION_REQUEST_BLOCK, generateConfirmActionHandler(requestBlockUser)],\n [SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],\n [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],\n [SELECTORS.ACTION_REQUEST_REMOVE_CONTACT, generateConfirmActionHandler(requestRemoveContact)],\n [SELECTORS.ACTION_REQUEST_DELETE_CONVERSATION, generateConfirmActionHandler(requestDeleteConversation)],\n [SELECTORS.ACTION_CANCEL_EDIT_MODE, handleCancelEditMode],\n [SELECTORS.ACTION_VIEW_CONTACT, generateHandleViewContact(namespace)],\n [SELECTORS.ACTION_VIEW_GROUP_INFO, generateHandleViewGroupInfo(namespace)],\n [SELECTORS.ACTION_CONFIRM_FAVOURITE, handleSetFavourite],\n [SELECTORS.ACTION_CONFIRM_MUTE, handleSetMuted],\n [SELECTORS.ACTION_CONFIRM_UNFAVOURITE, handleUnsetFavourite],\n [SELECTORS.ACTION_CONFIRM_UNMUTE, handleUnsetMuted]\n ];\n var bodyActivateHandlers = [\n [SELECTORS.ACTION_CANCEL_CONFIRM, generateConfirmActionHandler(cancelRequest)],\n [SELECTORS.ACTION_CONFIRM_BLOCK, generateConfirmActionHandler(blockUser)],\n [SELECTORS.ACTION_CONFIRM_UNBLOCK, generateConfirmActionHandler(unblockUser)],\n [SELECTORS.ACTION_CONFIRM_ADD_CONTACT, generateConfirmActionHandler(addContact)],\n [SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT, generateConfirmActionHandler(removeContact)],\n [SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES, generateConfirmActionHandler(deleteSelectedMessages)],\n [SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION, generateConfirmActionHandler(deleteConversation)],\n [SELECTORS.ACTION_OKAY_CONFIRM, generateConfirmActionHandler(cancelRequest)],\n [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],\n [SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST, generateConfirmActionHandler(acceptContactRequest)],\n [SELECTORS.ACTION_DECLINE_CONTACT_REQUEST, generateConfirmActionHandler(declineContactRequest)],\n [SELECTORS.MESSAGE, handleSelectMessage],\n [SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE, handleDeleteMessagesForAllUsersToggle],\n [SELECTORS.RETRY_SEND, handleRetrySendMessage]\n ];\n var footerActivateHandlers = [\n [SELECTORS.SEND_MESSAGE_BUTTON, handleSendMessage],\n [SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON, handleToggleEmojiPicker],\n [SELECTORS.ACTION_REQUEST_DELETE_SELECTED_MESSAGES, generateConfirmActionHandler(requestDeleteSelectedMessages)],\n [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],\n [SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],\n ];\n\n AutoRows.init(footer);\n\n if (emojiAutoCompleteContainer.length) {\n initialiseEmojiAutoComplete(\n emojiAutoCompleteContainer[0],\n messageTextArea[0],\n function(hasSuggestions) {\n var newState = StateManager.setShowEmojiAutoComplete(viewState, hasSuggestions);\n render(newState);\n },\n function(emoji) {\n var newState = StateManager.setShowEmojiAutoComplete(viewState, false);\n render(newState);\n\n messageTextArea.focus();\n var cursorPos = messageTextArea.prop('selectionStart');\n var currentText = messageTextArea.val();\n var textBefore = currentText.substring(0, cursorPos).replace(/\\S*$/, '');\n var textAfter = currentText.substring(cursorPos).replace(/^\\S*/, '');\n\n messageTextArea.val(textBefore + emoji + textAfter);\n // Set the cursor position to after the inserted emoji.\n messageTextArea.prop('selectionStart', textBefore.length + emoji.length);\n messageTextArea.prop('selectionEnd', textBefore.length + emoji.length);\n }\n );\n }\n\n if (emojiPickerElement.length) {\n initialiseEmojiPicker(emojiPickerElement[0], function(emoji) {\n var newState = StateManager.setShowEmojiPicker(viewState, !viewState.showEmojiPicker);\n render(newState);\n\n messageTextArea.focus();\n var cursorPos = messageTextArea.prop('selectionStart');\n var currentText = messageTextArea.val();\n var textBefore = currentText.substring(0, cursorPos);\n var textAfter = currentText.substring(cursorPos, currentText.length);\n\n messageTextArea.val(textBefore + emoji + textAfter);\n // Set the cursor position to after the inserted emoji.\n messageTextArea.prop('selectionStart', cursorPos + emoji.length);\n messageTextArea.prop('selectionEnd', cursorPos + emoji.length);\n });\n }\n\n CustomEvents.define(header, [\n CustomEvents.events.activate\n ]);\n CustomEvents.define(body, [\n CustomEvents.events.activate\n ]);\n CustomEvents.define(footer, [\n CustomEvents.events.activate,\n CustomEvents.events.enter,\n CustomEvents.events.escape\n ]);\n CustomEvents.define(messagesContainer, [\n CustomEvents.events.scrollTop,\n CustomEvents.events.scrollLock\n ]);\n\n messagesContainer.on(CustomEvents.events.scrollTop, function(e, data) {\n var hasMembers = Object.keys(viewState.members).length > 1;\n\n if (!isResetting && !isLoadingMoreMessages && !hasLoadedAllMessages() && hasMembers) {\n isLoadingMoreMessages = true;\n var newState = StateManager.setLoadingMessages(viewState, true);\n render(newState);\n\n loadMessages(viewState.id, LOAD_MESSAGE_LIMIT, getMessagesOffset(), NEWEST_FIRST, [])\n .then(function() {\n isLoadingMoreMessages = false;\n setMessagesOffset(getMessagesOffset() + LOAD_MESSAGE_LIMIT);\n return;\n })\n .catch(function(error) {\n isLoadingMoreMessages = false;\n Notification.exception(error);\n });\n }\n\n data.originalEvent.preventDefault();\n });\n\n headerActivateHandlers.forEach(function(handler) {\n var selector = handler[0];\n var handlerFunction = handler[1];\n header.on(CustomEvents.events.activate, selector, handlerFunction);\n });\n\n bodyActivateHandlers.forEach(function(handler) {\n var selector = handler[0];\n var handlerFunction = handler[1];\n body.on(CustomEvents.events.activate, selector, handlerFunction);\n });\n\n footerActivateHandlers.forEach(function(handler) {\n var selector = handler[0];\n var handlerFunction = handler[1];\n footer.on(CustomEvents.events.activate, selector, handlerFunction);\n });\n\n footer.on(CustomEvents.events.enter, SELECTORS.MESSAGE_TEXT_AREA, function(e, data) {\n var enterToSend = footer.attr('data-enter-to-send');\n if (enterToSend && enterToSend != 'false' && enterToSend != '0') {\n handleSendMessage(e, data);\n }\n });\n\n footer.on(CustomEvents.events.escape, SELECTORS.EMOJI_PICKER_CONTAINER, handleToggleEmojiPicker);\n $(document.body).on('click', handleCloseEmojiPicker);\n\n PubSub.subscribe(MessageDrawerEvents.ROUTE_CHANGED, function(newRouteData) {\n if (newMessagesPollTimer) {\n if (newRouteData.route != MessageDrawerRoutes.VIEW_CONVERSATION) {\n newMessagesPollTimer.stop();\n }\n }\n });\n };\n\n /**\n * Reset the timer that polls for new messages.\n *\n * @param {Number} conversationId The conversation id\n */\n var resetMessagePollTimer = function(conversationId) {\n if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\n }\n\n newMessagesPollTimer = new BackOffTimer(\n getLoadNewMessagesCallback(conversationId, NEWEST_FIRST),\n BackOffTimer.getIncrementalCallback(\n viewState.messagePollMin * MILLISECONDS_IN_SEC,\n MILLISECONDS_IN_SEC,\n viewState.messagePollMax * MILLISECONDS_IN_SEC,\n viewState.messagePollAfterMax * MILLISECONDS_IN_SEC\n )\n );\n\n newMessagesPollTimer.start();\n };\n\n /**\n * Reset the state to the initial state and render the UI.\n *\n * @param {Object} body Conversation body container element.\n * @param {Number|null} conversationId The conversation id.\n * @param {Object} loggedInUserProfile The logged in user's profile.\n */\n var resetState = function(body, conversationId, loggedInUserProfile) {\n // Reset all of the states back to the beginning if we're loading a new\n // conversation.\n if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\n }\n loadedAllMessages = false;\n messagesOffset = 0;\n newMessagesPollTimer = null;\n isRendering = false;\n renderBuffer = [];\n isResetting = true;\n isSendingMessage = false;\n isDeletingConversationContent = false;\n sendMessageBuffer = [];\n\n var loggedInUserId = loggedInUserProfile.id;\n var midnight = parseInt(body.attr('data-midnight'), 10);\n var messagePollMin = parseInt(body.attr('data-message-poll-min'), 10);\n var messagePollMax = parseInt(body.attr('data-message-poll-max'), 10);\n var messagePollAfterMax = parseInt(body.attr('data-message-poll-after-max'), 10);\n var initialState = StateManager.buildInitialState(\n midnight,\n loggedInUserId,\n conversationId,\n messagePollMin,\n messagePollMax,\n messagePollAfterMax\n );\n\n if (!viewState) {\n viewState = initialState;\n }\n\n render(initialState);\n };\n\n /**\n * Load a new empty private conversation between two users or self-conversation.\n *\n * @param {Object} body Conversation body container element.\n * @param {Object} loggedInUserProfile The logged in user's profile.\n * @param {Int} otherUserId The other user's id.\n * @return {Promise} Renderer promise.\n */\n var resetNoConversation = function(body, loggedInUserProfile, otherUserId) {\n // Always reset the state back to the initial state so that the\n // state manager and patcher can work correctly.\n resetState(body, null, loggedInUserProfile);\n\n var resetNoConversationPromise = null;\n\n if (loggedInUserProfile.id != otherUserId) {\n // Private conversation between two different users.\n resetNoConversationPromise = Repository.getConversationBetweenUsers(\n loggedInUserProfile.id,\n otherUserId,\n true,\n true,\n 0,\n 0,\n LOAD_MESSAGE_LIMIT,\n 0,\n NEWEST_FIRST\n );\n } else {\n // Self conversation.\n resetNoConversationPromise = Repository.getSelfConversation(\n loggedInUserProfile.id,\n LOAD_MESSAGE_LIMIT,\n 0,\n NEWEST_FIRST\n );\n }\n\n return resetNoConversationPromise.then(function(conversation) {\n // Looks like we have a conversation after all! Let's use that.\n return resetByConversation(body, conversation, loggedInUserProfile);\n })\n .catch(function() {\n // Can't find a conversation. Oh well. Just load up a blank one.\n return loadEmptyPrivateConversation(loggedInUserProfile, otherUserId);\n });\n };\n\n /**\n * Load new messages into the conversation based on a time interval.\n *\n * @param {Object} body Conversation body container element.\n * @param {Number} conversationId The conversation id.\n * @param {Object} loggedInUserProfile The logged in user's profile.\n * @return {Promise} Renderer promise.\n */\n var resetById = function(body, conversationId, loggedInUserProfile) {\n var cache = null;\n if (conversationId in stateCache) {\n cache = stateCache[conversationId];\n }\n\n // Always reset the state back to the initial state so that the\n // state manager and patcher can work correctly.\n resetState(body, conversationId, loggedInUserProfile);\n\n var promise = $.Deferred().resolve({}).promise();\n if (cache) {\n // We've seen this conversation before so there is no need to\n // send any network requests.\n var newState = cache.state;\n // Reset some loading states just in case they were left weirdly.\n newState = StateManager.setLoadingMessages(newState, false);\n newState = StateManager.setLoadingMembers(newState, false);\n setMessagesOffset(cache.messagesOffset);\n setLoadedAllMessages(cache.loadedAllMessages);\n render(newState);\n } else {\n promise = loadNewConversation(\n conversationId,\n loggedInUserProfile,\n LOAD_MESSAGE_LIMIT,\n 0,\n NEWEST_FIRST\n );\n }\n\n return promise.then(function() {\n return resetMessagePollTimer(conversationId);\n });\n };\n\n /**\n * Load new messages into the conversation based on a time interval.\n *\n * @param {Object} body Conversation body container element.\n * @param {Object} conversation The conversation.\n * @param {Object} loggedInUserProfile The logged in user's profile.\n * @return {Promise} Renderer promise.\n */\n var resetByConversation = function(body, conversation, loggedInUserProfile) {\n var cache = null;\n if (conversation.id in stateCache) {\n cache = stateCache[conversation.id];\n }\n\n // Always reset the state back to the initial state so that the\n // state manager and patcher can work correctly.\n resetState(body, conversation.id, loggedInUserProfile);\n\n var promise = $.Deferred().resolve({}).promise();\n if (cache) {\n // We've seen this conversation before so there is no need to\n // send any network requests.\n var newState = cache.state;\n // Reset some loading states just in case they were left weirdly.\n newState = StateManager.setLoadingMessages(newState, false);\n newState = StateManager.setLoadingMembers(newState, false);\n setMessagesOffset(cache.messagesOffset);\n setLoadedAllMessages(cache.loadedAllMessages);\n render(newState);\n } else {\n promise = loadExistingConversation(\n conversation,\n loggedInUserProfile,\n LOAD_MESSAGE_LIMIT,\n NEWEST_FIRST\n );\n }\n\n return promise.then(function() {\n return resetMessagePollTimer(conversation.id);\n });\n };\n\n /**\n * Setup the conversation page. This is a rather complex function because there are a\n * few combinations of arguments that can be provided to this function to show the\n * conversation.\n *\n * There are:\n * 1.) A conversation object with no action or other user id (e.g. from the overview page)\n * 2.) A conversation id with no action or other user id (e.g. from the contacts page)\n * 3.) No conversation/id with an action and other other user id. (e.g. from contact page)\n *\n * @param {string} namespace The route namespace.\n * @param {Object} header Conversation header container element.\n * @param {Object} body Conversation body container element.\n * @param {Object} footer Conversation footer container element.\n * @param {Object|Number|null} conversationOrId Conversation or id or null\n * @param {String} action An action to take on the conversation\n * @param {Number} otherUserId The other user id for a private conversation\n * @return {Object} jQuery promise\n */\n var show = function(namespace, header, body, footer, conversationOrId, action, otherUserId) {\n var conversation = null;\n var conversationId = null;\n\n // Check what we were given to identify the conversation.\n if (conversationOrId && conversationOrId !== null && typeof conversationOrId == 'object') {\n conversation = conversationOrId;\n conversationId = parseInt(conversation.id, 10);\n } else {\n conversation = null;\n conversationId = parseInt(conversationOrId, 10);\n conversationId = isNaN(conversationId) ? null : conversationId;\n }\n\n if (!conversationId && action && otherUserId) {\n // If we didn't get a conversation id got a user id then let's see if we've\n // previously loaded a private conversation with this user.\n conversationId = getCachedPrivateConversationIdFromUserId(otherUserId);\n }\n\n // Set attributes to aid in the restoration of unsent messages.\n setConversationAttributes(footer, conversationId, otherUserId);\n\n // This is a new conversation if:\n // 1. We don't already have a state\n // 2. The given conversation doesn't match the one currently loaded\n // 3. We have a view state without a conversation id and we weren't given one\n // but we were given a different other user id. This happens when the user\n // goes from viewing a user that they haven't yet initialised a conversation\n // with to viewing a different user that they also haven't initialised a\n // conversation with.\n var isNewConversation = !viewState || (viewState.id != conversationId) || (otherUserId && otherUserId != getOtherUserId());\n\n if (!body.attr('data-init')) {\n // Generate the render function to bind the header, body, and footer\n // elements to it so that we don't need to pass them around this module.\n render = generateRenderFunction(header, body, footer, isNewConversation);\n registerEventListeners(namespace, header, body, footer);\n body.attr('data-init', true);\n }\n\n if (isNewConversation) {\n var renderPromise = null;\n var loggedInUserProfile = getLoggedInUserProfile(body);\n\n if (conversation) {\n renderPromise = resetByConversation(body, conversation, loggedInUserProfile, otherUserId);\n } else if (conversationId) {\n renderPromise = resetById(body, conversationId, loggedInUserProfile, otherUserId);\n } else {\n renderPromise = resetNoConversation(body, loggedInUserProfile, otherUserId);\n }\n\n return renderPromise\n .then(function() {\n isResetting = false;\n // Focus the first element that can receieve it in the header.\n header.find(Constants.SELECTORS.CAN_RECEIVE_FOCUS).first().focus();\n return;\n })\n .catch(function(error) {\n isResetting = false;\n Notification.exception(error);\n });\n }\n\n // We're not loading a new conversation so we should reset the poll timer to try to load\n // new messages.\n resetMessagePollTimer(conversationId);\n\n if (viewState.type == CONVERSATION_TYPES.PRIVATE && action) {\n // There are special actions that the user can perform in a private (aka 1-to-1)\n // conversation.\n var currentOtherUserId = getOtherUserId();\n\n switch (action) {\n case 'block':\n return requestBlockUser(currentOtherUserId);\n case 'unblock':\n return requestUnblockUser(currentOtherUserId);\n case 'add-contact':\n return requestAddContact(currentOtherUserId);\n case 'remove-contact':\n return requestRemoveContact(currentOtherUserId);\n }\n }\n\n // Final fallback to return a promise if we didn't need to do anything.\n return $.Deferred().resolve().promise();\n };\n\n /**\n * String describing this page used for aria-labels.\n *\n * @return {Object} jQuery promise\n */\n var description = function() {\n return Str.get_string('messagedrawerviewconversation', 'core_message', viewState.name);\n };\n\n /**\n * Set some attributes that will help reboot a conversation.\n *\n * These attributes aid in the storage and retrieval of unsent messages.\n * When a conversationid is not present, otheruserid can be used to create a conversation.\n *\n * @param {Object} element The element to target.\n * @param {Number|null} conversationId The conversationid.\n * @param {Number|null} otherUserId The otheruserid.\n */\n const setConversationAttributes = function(element, conversationId, otherUserId) {\n element.removeAttr('data-conversation-id');\n element.removeAttr('data-other-user-id');\n if (conversationId) {\n element.attr('data-conversation-id', conversationId);\n } else if (otherUserId) {\n element.attr('data-other-user-id', otherUserId);\n }\n };\n\n return {\n show: show,\n description: description\n };\n});\n"],"names":["define","$","AutoRows","BackOffTimer","CustomEvents","Notification","Pending","PubSub","Str","Repository","MessageDrawerEvents","Constants","Patcher","Renderer","StateManager","MessageDrawerRouter","MessageDrawerRoutes","initialiseEmojiAutoComplete","initialiseEmojiPicker","stateCache","viewState","loadedAllMessages","messagesOffset","newMessagesPollTimer","isRendering","renderBuffer","isResetting","isSendingMessage","isDeletingConversationContent","sendMessageBuffer","render","renderers","NEWEST_FIRST","NEWEST_MESSAGES_FIRST","LOAD_MESSAGE_LIMIT","MILLISECONDS_IN_SEC","SELECTORS","CONVERSATION_TYPES","getOtherUserId","type","PUBLIC","loggedInUserId","SELF","otherUserIds","Object","keys","members","filter","userId","length","getMessagesOffset","setMessagesOffset","value","id","hasLoadedAllMessages","setLoadedAllMessages","formatConversationForEvent","state","name","subname","imageUrl","isFavourite","isMuted","totalMemberCount","messages","map","message","extend","formattedMember","contactrequests","request","updateStateFromConversation","conversation","otherUser","PRIVATE","otherUsers","member","imageurl","fullname","profileimageurl","newState","addMembers","setName","setSubname","setType","setImageUrl","setTotalMemberCount","membercount","setIsFavourite","isfavourite","setIsMuted","ismuted","addMessages","setCanDeleteMessagesForAllUsers","candeletemessagesforallusers","loadMessages","conversationId","limit","offset","newestFirst","ignoreList","timeFrom","getMessages","then","result","indexOf","parseInt","slice","membersToAdd","setLoadingMessages","catch","error","markConversationAsRead","pendingPromise","markAllConversationMessagesAsRead","markMessagesAsRead","publish","CONVERSATION_READ","resolve","requestBlockUser","cancelRequest","addPendingBlockUsersById","blockUser","setLoadingConfirmAction","profile","removePendingBlockUsersById","CONTACT_BLOCKED","requestUnblockUser","addPendingUnblockUsersById","unblockUser","removePendingUnblockUsersById","CONTACT_UNBLOCKED","requestRemoveContact","addPendingRemoveContactsById","removeContact","deleteContacts","profiles","removePendingRemoveContactsById","CONTACT_REMOVED","requestAddContact","addPendingAddContactsById","addContact","createContactRequest","response","Error","warnings","removePendingAddContactsById","addContactRequests","requestDeleteSelectedMessages","selectedMessageIds","addPendingDeleteMessagesById","deleteSelectedMessages","messageIds","pendingDeleteMessageIds","sentMessages","sendState","deleteMessagesPromise","Deferred","promise","sentMessageIds","deleteMessagesForAllUsers","deleteMessages","stop","removeMessagesById","removePendingDeleteMessagesById","removeSelectedMessagesById","setDeleteMessagesForAllUsers","prevLastMessage","newLastMessage","CONVERSATION_NEW_LAST_MESSAGE","CONVERSATION_DELETED","exception","requestDeleteConversation","setPendingDeleteConversation","deleteConversation","removeMessages","acceptContactRequest","requests","requesteduserid","removeContactRequests","CONTACT_ADDED","CONTACT_REQUEST_ACCEPTED","declineContactRequest","CONTACT_REQUEST_DECLINED","processSendMessageBuffer","messagesToSend","newConversationId","messagesText","text","sendMessagePromise","newCanDeleteMessagesForAllUsers","sendMessagesToConversation","otherUserId","sendMessagesToUser","conversationid","newMessageIds","data","selectedToRemove","selectedToAdd","forEach","oldMessage","index","newMessage","push","updateMessages","setMessagesSendSuccessById","addSelectedMessagesById","setId","resetMessagePollTimer","CONVERSATION_CREATED","e","errorMessage","get_string","handleFailedMessages","setMessagesSendFailById","finalError","previewText","plaintext","replace","processRenderBuffer","header","body","footer","renderable","shift","renderPromises","renderFunc","patch","when","apply","deferred","reject","generateConfirmActionHandler","actionCallback","loadingConfirmAction","originalEvent","preventDefault","handleSendMessage","textArea","target","closest","FOOTER_CONTAINER","find","MESSAGE_TEXT_AREA","val","trim","Date","now","loadingmessage","useridfrom","timecreated","document","querySelector","removeAttribute","sendMessage","focus","handleSelectMessage","selection","window","getSelection","toString","is","messageId","toggleSelectMessage","MESSAGE","attr","handleRetrySendMessage","setMessagesSendPendingById","retrySendMessage","stopPropagation","handleCancelEditMode","cancelEditMode","generateHandleViewContact","namespace","go","VIEW_CONTACT","handleSetFavourite","setFavouriteConversations","CONVERSATION_SET_FAVOURITE","handleUnsetFavourite","unsetFavouriteConversations","CONVERSATION_UNSET_FAVOURITE","handleSetMuted","setMutedConversations","CONVERSATION_SET_MUTED","handleUnsetMuted","unsetMutedConversations","CONVERSATION_UNSET_MUTED","handleDeleteMessagesForAllUsersToggle","newValue","prop","generateHandleViewGroupInfo","VIEW_GROUP_INFO","handleToggleEmojiPicker","setShowEmojiPicker","showEmojiPicker","handleCloseEmojiPicker","EMOJI_PICKER_CONTAINER","TOGGLE_EMOJI_PICKER_BUTTON","registerEventListeners","isLoadingMoreMessages","messagesContainer","MESSAGES_CONTAINER","getMessagesContainer","emojiPickerElement","EMOJI_PICKER","emojiAutoCompleteContainer","EMOJI_AUTO_COMPLETE_CONTAINER","messageTextArea","headerActivateHandlers","ACTION_REQUEST_BLOCK","ACTION_REQUEST_UNBLOCK","ACTION_REQUEST_ADD_CONTACT","ACTION_REQUEST_REMOVE_CONTACT","ACTION_REQUEST_DELETE_CONVERSATION","ACTION_CANCEL_EDIT_MODE","ACTION_VIEW_CONTACT","ACTION_VIEW_GROUP_INFO","ACTION_CONFIRM_FAVOURITE","ACTION_CONFIRM_MUTE","ACTION_CONFIRM_UNFAVOURITE","ACTION_CONFIRM_UNMUTE","bodyActivateHandlers","ACTION_CANCEL_CONFIRM","ACTION_CONFIRM_BLOCK","ACTION_CONFIRM_UNBLOCK","ACTION_CONFIRM_ADD_CONTACT","ACTION_CONFIRM_REMOVE_CONTACT","ACTION_CONFIRM_DELETE_SELECTED_MESSAGES","ACTION_CONFIRM_DELETE_CONVERSATION","ACTION_OKAY_CONFIRM","ACTION_ACCEPT_CONTACT_REQUEST","ACTION_DECLINE_CONTACT_REQUEST","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE","RETRY_SEND","footerActivateHandlers","SEND_MESSAGE_BUTTON","ACTION_REQUEST_DELETE_SELECTED_MESSAGES","init","hasSuggestions","setShowEmojiAutoComplete","emoji","cursorPos","currentText","textBefore","substring","textAfter","events","activate","enter","escape","scrollTop","scrollLock","on","hasMembers","handler","selector","handlerFunction","enterToSend","subscribe","ROUTE_CHANGED","newRouteData","route","VIEW_CONVERSATION","mostRecentMessage","lastTimeCreated","timeCreated","ignoreMessageIds","i","restart","getLoadNewMessagesCallback","getIncrementalCallback","messagePollMin","messagePollMax","messagePollAfterMax","start","resetState","loggedInUserProfile","midnight","initialState","buildInitialState","resetNoConversation","getConversationBetweenUsers","getSelfConversation","resetByConversation","conversationType","setLoadingMembers","getMemberInfo","loadEmptyPrivateConversation","resetById","cache","messageLimit","messageOffset","getConversation","concat","loadNewConversation","messageCount","hasLoadedEnoughMessages","loadExistingConversation","setConversationAttributes","element","removeAttr","show","conversationOrId","action","isNaN","reduce","carry","isNewConversation","rendererFunc","buildPatch","generateRenderFunction","profileimageurlsmall","isonline","showonlinestatus","isblocked","iscontact","isdeleted","canmessage","canmessageevenifblocked","requirescontact","cancreatecontact","getLoggedInUserProfile","CAN_RECEIVE_FOCUS","first","currentOtherUserId","description"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"message_drawer_view_conversation.min.js","sources":["../src/message_drawer_view_conversation.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 .\n\n/**\n * Controls the conversation page in the message drawer.\n *\n * This function handles all of the user actions that the user can take\n * when interacting with the conversation page.\n *\n * It maintains a view state which is a data representation of the view\n * and only operates on that data.\n *\n * The view state is immutable and should never be modified directly. Instead\n * all changes to the view state should be done using the StateManager which\n * will generate a new version of the view state with the requested changes.\n *\n * After any changes to the view state the module will call the render function\n * to ask the renderer to update the UI.\n *\n * General rules for this module:\n * 1.) Never modify viewState directly. All changes should be via the StateManager.\n * 2.) Call render() with the new state when you want to update the UI\n * 3.) Never modify the UI directly in this module. This module is only concerned\n * with the data in the view state.\n *\n * The general flow for a user interaction will be something like:\n * User interaction: User clicks \"confirm block\" button to block the other user\n * 1.) This module is hears the click\n * 2.) This module sends a request to the server to block the user\n * 3.) The server responds with the new user profile\n * 4.) This module generates a new state using the StateManager with the updated\n * user profile.\n * 5.) This module asks the Patcher to generate a patch from the current state and\n * the newly generated state. This patch tells the renderer what has changed\n * between the states.\n * 6.) This module gives the Renderer the generated patch. The renderer updates\n * the UI with changes according to the patch.\n *\n * @module core_message/message_drawer_view_conversation\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/auto_rows',\n 'core/backoff_timer',\n 'core/custom_interaction_events',\n 'core/notification',\n 'core/pending',\n 'core/pubsub',\n 'core/str',\n 'core_message/message_repository',\n 'core_message/message_drawer_events',\n 'core_message/message_drawer_view_conversation_constants',\n 'core_message/message_drawer_view_conversation_patcher',\n 'core_message/message_drawer_view_conversation_renderer',\n 'core_message/message_drawer_view_conversation_state_manager',\n 'core_message/message_drawer_router',\n 'core_message/message_drawer_routes',\n 'core/emoji/auto_complete',\n 'core/emoji/picker'\n],\nfunction(\n $,\n AutoRows,\n BackOffTimer,\n CustomEvents,\n Notification,\n Pending,\n PubSub,\n Str,\n Repository,\n MessageDrawerEvents,\n Constants,\n Patcher,\n Renderer,\n StateManager,\n MessageDrawerRouter,\n MessageDrawerRoutes,\n initialiseEmojiAutoComplete,\n initialiseEmojiPicker\n) {\n\n // Contains a cache of all view states that have been loaded so far\n // which saves us having to reload stuff with network requests when\n // switching between conversations.\n var stateCache = {};\n // The current data representation of the view.\n var viewState = null;\n var loadedAllMessages = false;\n var messagesOffset = 0;\n var newMessagesPollTimer = null;\n var isRendering = false;\n var renderBuffer = [];\n // If the UI is currently resetting.\n var isResetting = true;\n // If the UI is currently sending a message.\n var isSendingMessage = false;\n // If the UI is currently deleting a conversation.\n var isDeletingConversationContent = false;\n // A buffer of messages to send.\n var sendMessageBuffer = [];\n // These functions which will be generated when this module is\n // first called. See generateRenderFunction for details.\n var render = null;\n // The list of renderers that have been registered to render\n // this conversation. See generateRenderFunction for details.\n var renderers = [];\n\n var NEWEST_FIRST = Constants.NEWEST_MESSAGES_FIRST;\n var LOAD_MESSAGE_LIMIT = Constants.LOAD_MESSAGE_LIMIT;\n var MILLISECONDS_IN_SEC = Constants.MILLISECONDS_IN_SEC;\n var SELECTORS = Constants.SELECTORS;\n var CONVERSATION_TYPES = Constants.CONVERSATION_TYPES;\n\n /**\n * Get the other user userid.\n *\n * @return {Number} Userid.\n */\n var getOtherUserId = function() {\n if (!viewState || viewState.type == CONVERSATION_TYPES.PUBLIC) {\n return null;\n }\n\n var loggedInUserId = viewState.loggedInUserId;\n if (viewState.type == CONVERSATION_TYPES.SELF) {\n // It's a self-conversation, so the other user is the one logged in.\n return loggedInUserId;\n }\n\n var otherUserIds = Object.keys(viewState.members).filter(function(userId) {\n return loggedInUserId != userId;\n });\n\n return otherUserIds.length ? otherUserIds[0] : null;\n };\n\n /**\n * Search the cache to see if we've already loaded a private conversation\n * with the given user id.\n *\n * @param {Number} userId The id of the other user.\n * @return {Number|null} Conversation id.\n */\n var getCachedPrivateConversationIdFromUserId = function(userId) {\n return Object.keys(stateCache).reduce(function(carry, id) {\n if (!carry) {\n var state = stateCache[id].state;\n\n if (state.type != CONVERSATION_TYPES.PUBLIC) {\n if (userId in state.members) {\n // We've found a cached conversation for this user!\n carry = state.id;\n }\n }\n }\n\n return carry;\n }, null);\n };\n\n /**\n * Get profile info for logged in user.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object}\n */\n var getLoggedInUserProfile = function(body) {\n return {\n id: parseInt(body.attr('data-user-id'), 10),\n fullname: null,\n profileimageurl: null,\n profileimageurlsmall: null,\n isonline: null,\n showonlinestatus: null,\n isblocked: null,\n iscontact: null,\n isdeleted: null,\n canmessage: null,\n canmessageevenifblocked: null,\n requirescontact: null,\n contactrequests: [],\n cancreatecontact: null,\n };\n };\n\n /**\n * Get the messages offset value to load more messages.\n *\n * @return {Number}\n */\n var getMessagesOffset = function() {\n return messagesOffset;\n };\n\n /**\n * Set the messages offset value for loading more messages.\n *\n * @param {Number} value The offset value\n */\n var setMessagesOffset = function(value) {\n messagesOffset = value;\n stateCache[viewState.id].messagesOffset = value;\n };\n\n /**\n * Check if all messages have been loaded.\n *\n * @return {Bool}\n */\n var hasLoadedAllMessages = function() {\n return loadedAllMessages;\n };\n\n /**\n * Set whether all messages have been loaded or not.\n *\n * @param {Bool} value If all messages have been loaded.\n */\n var setLoadedAllMessages = function(value) {\n loadedAllMessages = value;\n stateCache[viewState.id].loadedAllMessages = value;\n };\n\n /**\n * Get the messages container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var getMessagesContainer = function(body) {\n return body.find(SELECTORS.MESSAGES_CONTAINER);\n };\n\n /**\n * Reformat the conversation for an event payload.\n *\n * @param {Object} state The view state.\n * @return {Object} New formatted conversation.\n */\n var formatConversationForEvent = function(state) {\n return {\n id: state.id,\n name: state.name,\n subname: state.subname,\n imageUrl: state.imageUrl,\n isFavourite: state.isFavourite,\n isMuted: state.isMuted,\n type: state.type,\n totalMemberCount: state.totalMemberCount,\n loggedInUserId: state.loggedInUserId,\n messages: state.messages.map(function(message) {\n return $.extend({}, message);\n }),\n members: Object.keys(state.members).map(function(id) {\n var formattedMember = $.extend({}, state.members[id]);\n formattedMember.contactrequests = state.members[id].contactrequests.map(function(request) {\n return $.extend({}, request);\n });\n return formattedMember;\n })\n };\n };\n\n /**\n * Load up an empty private conversation between the logged in user and the\n * other user. Sets all of the conversation details based on the other user.\n *\n * A conversation isn't created until the user sends the first message.\n *\n * @param {Object} loggedInUserProfile The logged in user profile.\n * @param {Number} otherUserId The other user id.\n * @return {Object} Profile returned from repository.\n */\n var loadEmptyPrivateConversation = function(loggedInUserProfile, otherUserId) {\n var loggedInUserId = loggedInUserProfile.id;\n // If the other user id is the same as the logged in user then this is a self\n // conversation.\n var conversationType = loggedInUserId == otherUserId ? CONVERSATION_TYPES.SELF : CONVERSATION_TYPES.PRIVATE;\n var newState = StateManager.setLoadingMembers(viewState, true);\n newState = StateManager.setLoadingMessages(newState, true);\n render(newState);\n\n return Repository.getMemberInfo(loggedInUserId, [otherUserId], true, true)\n .then(function(profiles) {\n if (profiles.length) {\n return profiles[0];\n } else {\n throw new Error('Unable to load other user profile');\n }\n })\n .then(function(profile) {\n // If the conversation is a self conversation then the profile loaded is the\n // logged in user so only add that to the members array.\n var members = conversationType == CONVERSATION_TYPES.SELF ? [profile] : [profile, loggedInUserProfile];\n var newState = StateManager.addMembers(viewState, members);\n newState = StateManager.setLoadingMembers(newState, false);\n newState = StateManager.setLoadingMessages(newState, false);\n newState = StateManager.setName(newState, profile.fullname);\n newState = StateManager.setType(newState, conversationType);\n newState = StateManager.setImageUrl(newState, profile.profileimageurl);\n newState = StateManager.setTotalMemberCount(newState, members.length);\n render(newState);\n return profile;\n })\n .catch(function(error) {\n var newState = StateManager.setLoadingMembers(viewState, false);\n render(newState);\n Notification.exception(error);\n });\n };\n\n /**\n * Create a new state from a conversation object.\n *\n * @param {Object} conversation The conversation object.\n * @param {Number} loggedInUserId The logged in user id.\n * @return {Object} new state.\n */\n var updateStateFromConversation = function(conversation, loggedInUserId) {\n var otherUser = null;\n if (conversation.type == CONVERSATION_TYPES.PRIVATE) {\n // For private conversations, remove current logged in user from the members list to get the other user.\n var otherUsers = conversation.members.filter(function(member) {\n return member.id != loggedInUserId;\n });\n otherUser = otherUsers.length ? otherUsers[0] : null;\n } else if (conversation.type == CONVERSATION_TYPES.SELF) {\n // Self-conversations have only one member.\n otherUser = conversation.members[0];\n }\n\n var name = conversation.name;\n var imageUrl = conversation.imageurl;\n\n if (conversation.type != CONVERSATION_TYPES.PUBLIC) {\n name = name || otherUser ? otherUser.fullname : '';\n imageUrl = imageUrl || otherUser ? otherUser.profileimageurl : '';\n }\n\n var newState = StateManager.addMembers(viewState, conversation.members);\n newState = StateManager.setName(newState, name);\n newState = StateManager.setSubname(newState, conversation.subname);\n newState = StateManager.setType(newState, conversation.type);\n newState = StateManager.setImageUrl(newState, imageUrl);\n newState = StateManager.setTotalMemberCount(newState, conversation.membercount);\n newState = StateManager.setIsFavourite(newState, conversation.isfavourite);\n newState = StateManager.setIsMuted(newState, conversation.ismuted);\n newState = StateManager.addMessages(newState, conversation.messages);\n newState = StateManager.setCanDeleteMessagesForAllUsers(newState, conversation.candeletemessagesforallusers);\n newState = StateManager.setCanSendMessageToConversation(newState, conversation.cansendmessagetoconversation);\n return newState;\n };\n\n /**\n * Get the details for a conversation from the conversation id.\n *\n * @param {Number} conversationId The conversation id.\n * @param {Object} loggedInUserProfile The logged in user profile.\n * @param {Number} messageLimit The number of messages to include.\n * @param {Number} messageOffset The number of messages to skip.\n * @param {Bool} newestFirst Order messages newest first.\n * @return {Object} Promise resolved when loaded.\n */\n var loadNewConversation = function(\n conversationId,\n loggedInUserProfile,\n messageLimit,\n messageOffset,\n newestFirst\n ) {\n var loggedInUserId = loggedInUserProfile.id;\n var newState = StateManager.setLoadingMembers(viewState, true);\n newState = StateManager.setLoadingMessages(newState, true);\n render(newState);\n\n return Repository.getConversation(\n loggedInUserId,\n conversationId,\n true,\n true,\n 0,\n 0,\n messageLimit + 1,\n messageOffset,\n newestFirst\n )\n .then(function(conversation) {\n if (conversation.messages.length > messageLimit) {\n conversation.messages = conversation.messages.slice(1);\n } else {\n setLoadedAllMessages(true);\n }\n\n setMessagesOffset(messageOffset + messageLimit);\n\n return conversation;\n })\n .then(function(conversation) {\n var hasLoggedInUser = conversation.members.filter(function(member) {\n return member.id == loggedInUserProfile.id;\n });\n\n if (hasLoggedInUser.length < 1) {\n conversation.members = conversation.members.concat([loggedInUserProfile]);\n }\n\n var newState = updateStateFromConversation(conversation, loggedInUserProfile.id);\n newState = StateManager.setLoadingMembers(newState, false);\n newState = StateManager.setLoadingMessages(newState, false);\n return render(newState)\n .then(function() {\n return conversation;\n });\n })\n .then(function() {\n return markConversationAsRead(conversationId);\n })\n .catch(function(error) {\n var newState = StateManager.setLoadingMembers(viewState, false);\n newState = StateManager.setLoadingMessages(newState, false);\n render(newState);\n Notification.exception(error);\n });\n };\n\n /**\n * Get the details for a conversation from and existing conversation object.\n *\n * @param {Object} conversation The conversation object.\n * @param {Object} loggedInUserProfile The logged in user profile.\n * @param {Number} messageLimit The number of messages to include.\n * @param {Bool} newestFirst Order messages newest first.\n * @return {Object} Promise resolved when loaded.\n */\n var loadExistingConversation = function(\n conversation,\n loggedInUserProfile,\n messageLimit,\n newestFirst\n ) {\n var hasLoggedInUser = conversation.members.filter(function(member) {\n return member.id == loggedInUserProfile.id;\n });\n\n if (hasLoggedInUser.length < 1) {\n conversation.members = conversation.members.concat([loggedInUserProfile]);\n }\n\n var messageCount = conversation.messages.length;\n var hasLoadedEnoughMessages = messageCount >= messageLimit;\n var newState = updateStateFromConversation(conversation, loggedInUserProfile.id);\n newState = StateManager.setLoadingMembers(newState, false);\n newState = StateManager.setLoadingMessages(newState, !hasLoadedEnoughMessages);\n var renderPromise = render(newState);\n\n return renderPromise.then(function() {\n if (!hasLoadedEnoughMessages) {\n // We haven't got enough messages so let's load some more.\n return loadMessages(conversation.id, messageLimit, messageCount, newestFirst, []);\n } else {\n // We've got enough messages. No need to load any more for now.\n return {messages: conversation.messages};\n }\n })\n .then(function() {\n var messages = viewState.messages;\n // Update the offset to reflect the number of messages we've loaded.\n setMessagesOffset(messages.length);\n markConversationAsRead(viewState.id);\n\n return messages;\n })\n .catch(Notification.exception);\n };\n\n /**\n * Load messages for this conversation and pass them to the renderer.\n *\n * @param {Number} conversationId Conversation id.\n * @param {Number} limit Number of messages to load.\n * @param {Number} offset Get messages from offset.\n * @param {Bool} newestFirst Get newest messages first.\n * @param {Array} ignoreList Ignore any messages with ids in this list.\n * @param {Number|null} timeFrom Only get messages from this time onwards.\n * @return {Promise} renderer promise.\n */\n var loadMessages = function(conversationId, limit, offset, newestFirst, ignoreList, timeFrom) {\n return Repository.getMessages(\n viewState.loggedInUserId,\n conversationId,\n limit ? limit + 1 : limit,\n offset,\n newestFirst,\n timeFrom\n )\n .then(function(result) {\n // Prevent older requests from contaminating the current view.\n if (result.id != viewState.id) {\n result.messages = [];\n // Purge old conversation cache to prevent messages lose.\n if (result.id in stateCache) {\n delete stateCache[result.id];\n }\n }\n\n return result;\n })\n .then(function(result) {\n if (result.messages.length && ignoreList.length) {\n result.messages = result.messages.filter(function(message) {\n // Skip any messages in our ignore list.\n return ignoreList.indexOf(parseInt(message.id, 10)) < 0;\n });\n }\n\n return result;\n })\n .then(function(result) {\n if (!limit) {\n return result;\n } else if (result.messages.length > limit) {\n // Ignore the last result which was just to test if there are more\n // to load.\n result.messages = result.messages.slice(0, -1);\n } else {\n setLoadedAllMessages(true);\n }\n\n return result;\n })\n .then(function(result) {\n var membersToAdd = result.members.filter(function(member) {\n return !(member.id in viewState.members);\n });\n var newState = StateManager.addMembers(viewState, membersToAdd);\n newState = StateManager.addMessages(newState, result.messages);\n newState = StateManager.setLoadingMessages(newState, false);\n return render(newState)\n .then(function() {\n return result;\n });\n })\n .catch(function(error) {\n var newState = StateManager.setLoadingMessages(viewState, false);\n render(newState);\n // Re-throw the error for other error handlers.\n throw error;\n });\n };\n\n /**\n * Create a callback function for getting new messages for this conversation.\n *\n * @param {Number} conversationId Conversation id.\n * @param {Bool} newestFirst Show newest messages first\n * @return {Function} Callback function that returns a renderer promise.\n */\n var getLoadNewMessagesCallback = function(conversationId, newestFirst) {\n return function() {\n var messages = viewState.messages;\n var mostRecentMessage = messages.length ? messages[messages.length - 1] : null;\n var lastTimeCreated = mostRecentMessage ? mostRecentMessage.timeCreated : null;\n\n if (lastTimeCreated && !isResetting && !isSendingMessage && !isDeletingConversationContent) {\n // There may be multiple messages with the same time created value since\n // the accuracy is only down to the second. The server will include these\n // messages in the result (since it does a >= comparison on time from) so\n // we need to filter them back out of the result so that we're left only\n // with the new messages.\n var ignoreMessageIds = [];\n for (var i = messages.length - 1; i >= 0; i--) {\n var message = messages[i];\n if (message.timeCreated === lastTimeCreated) {\n ignoreMessageIds.push(message.id);\n } else {\n // Since the messages are ordered in ascending order of time created\n // we can break as soon as we hit a message with a different time created\n // because we know all other messages will have lower values.\n break;\n }\n }\n\n return loadMessages(\n conversationId,\n 0,\n 0,\n newestFirst,\n ignoreMessageIds,\n lastTimeCreated\n )\n .then(function(result) {\n if (result.messages.length) {\n // If we found some results then restart the polling timer\n // because the other user might be sending messages.\n newMessagesPollTimer.restart();\n // We've also got a new last message so publish that for other\n // components to update.\n var conversation = formatConversationForEvent(viewState);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation);\n return markConversationAsRead(conversationId);\n } else {\n return result;\n }\n });\n }\n\n return $.Deferred().resolve().promise();\n };\n };\n\n /**\n * Mark a conversation as read.\n *\n * @param {Number} conversationId The conversation id.\n * @return {Promise} The renderer promise.\n */\n var markConversationAsRead = function(conversationId) {\n var loggedInUserId = viewState.loggedInUserId;\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:markConversationAsRead');\n\n return Repository.markAllConversationMessagesAsRead(loggedInUserId, conversationId)\n .then(function() {\n var newState = StateManager.markMessagesAsRead(viewState, viewState.messages);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_READ, conversationId);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager there is request to block a user and run the renderer\n * to show the block user dialogue.\n *\n * @param {Number} userId User id.\n */\n var requestBlockUser = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.addPendingBlockUsersById(viewState, [userId]);\n render(newState);\n };\n\n /**\n * Send the repository a request to block a user, update the statemanager and publish\n * a contact has been blocked.\n *\n * @param {Number} userId User id of user to block.\n * @return {Promise} Renderer promise.\n */\n var blockUser = function(userId) {\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:blockUser');\n\n render(newState);\n\n return Repository.blockUser(viewState.loggedInUserId, userId)\n .then(function(profile) {\n var newState = StateManager.addMembers(viewState, [profile]);\n newState = StateManager.removePendingBlockUsersById(newState, [userId]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n PubSub.publish(MessageDrawerEvents.CONTACT_BLOCKED, userId);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager there is a request to unblock a user and run the renderer\n * to show the unblock user dialogue.\n *\n * @param {Number} userId User id of user to unblock.\n */\n var requestUnblockUser = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.addPendingUnblockUsersById(viewState, [userId]);\n render(newState);\n };\n\n /**\n * Send the repository a request to unblock a user, update the statemanager and publish\n * a contact has been unblocked.\n *\n * @param {Number} userId User id of user to unblock.\n * @return {Promise} Renderer promise.\n */\n var unblockUser = function(userId) {\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:unblockUser');\n render(newState);\n\n return Repository.unblockUser(viewState.loggedInUserId, userId)\n .then(function(profile) {\n var newState = StateManager.addMembers(viewState, [profile]);\n newState = StateManager.removePendingUnblockUsersById(newState, [userId]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n PubSub.publish(MessageDrawerEvents.CONTACT_UNBLOCKED, userId);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager there is a request to remove a user from the contact list\n * and run the renderer to show the remove user from contacts dialogue.\n *\n * @param {Number} userId User id of user to remove from contacts.\n */\n var requestRemoveContact = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.addPendingRemoveContactsById(viewState, [userId]);\n render(newState);\n };\n\n /**\n * Send the repository a request to remove a user from the contacts list. update the statemanager\n * and publish a contact has been removed.\n *\n * @param {Number} userId User id of user to remove from contacts.\n * @return {Promise} Renderer promise.\n */\n var removeContact = function(userId) {\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:removeContact');\n render(newState);\n\n return Repository.deleteContacts(viewState.loggedInUserId, [userId])\n .then(function(profiles) {\n var newState = StateManager.addMembers(viewState, profiles);\n newState = StateManager.removePendingRemoveContactsById(newState, [userId]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n PubSub.publish(MessageDrawerEvents.CONTACT_REMOVED, userId);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager there is a request to add a user to the contact list\n * and run the renderer to show the add user to contacts dialogue.\n *\n * @param {Number} userId User id of user to add to contacts.\n */\n var requestAddContact = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.addPendingAddContactsById(viewState, [userId]);\n render(newState);\n };\n\n /**\n * Send the repository a request to add a user to the contacts list. update the statemanager\n * and publish a contact has been added.\n *\n * @param {Number} userId User id of user to add to contacts.\n * @return {Promise} Renderer promise.\n */\n var addContact = function(userId) {\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:addContactRequests');\n render(newState);\n\n return Repository.createContactRequest(viewState.loggedInUserId, userId)\n .then(function(response) {\n if (!response.request) {\n throw new Error(response.warnings[0].message);\n }\n\n return response.request;\n })\n .then(function(request) {\n var newState = StateManager.removePendingAddContactsById(viewState, [userId]);\n newState = StateManager.addContactRequests(newState, [request]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Set the current conversation as a favourite conversation.\n *\n * @return {Promise} Renderer promise.\n */\n var setFavourite = function() {\n var userId = viewState.loggedInUserId;\n var conversationId = viewState.id;\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:setFavourite');\n\n return Repository.setFavouriteConversations(userId, [conversationId])\n .then(function() {\n var newState = StateManager.setIsFavourite(viewState, true);\n return render(newState);\n })\n .then(function() {\n return PubSub.publish(\n MessageDrawerEvents.CONVERSATION_SET_FAVOURITE,\n formatConversationForEvent(viewState)\n );\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Unset the current conversation as a favourite conversation.\n *\n * @return {Promise} Renderer promise.\n */\n var unsetFavourite = function() {\n var userId = viewState.loggedInUserId;\n var conversationId = viewState.id;\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:unsetFavourite');\n\n return Repository.unsetFavouriteConversations(userId, [conversationId])\n .then(function() {\n var newState = StateManager.setIsFavourite(viewState, false);\n return render(newState);\n })\n .then(function() {\n return PubSub.publish(\n MessageDrawerEvents.CONVERSATION_UNSET_FAVOURITE,\n formatConversationForEvent(viewState)\n );\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Set the current conversation as a muted conversation.\n *\n * @return {Promise} Renderer promise.\n */\n var setMuted = function() {\n var userId = viewState.loggedInUserId;\n var conversationId = viewState.id;\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:markConversationAsRead');\n\n return Repository.setMutedConversations(userId, [conversationId])\n .then(function() {\n var newState = StateManager.setIsMuted(viewState, true);\n return render(newState);\n })\n .then(function() {\n return PubSub.publish(\n MessageDrawerEvents.CONVERSATION_SET_MUTED,\n formatConversationForEvent(viewState)\n );\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Unset the current conversation as a muted conversation.\n *\n * @return {Promise} Renderer promise.\n */\n var unsetMuted = function() {\n var userId = viewState.loggedInUserId;\n var conversationId = viewState.id;\n\n return Repository.unsetMutedConversations(userId, [conversationId])\n .then(function() {\n var newState = StateManager.setIsMuted(viewState, false);\n return render(newState);\n })\n .then(function() {\n return PubSub.publish(\n MessageDrawerEvents.CONVERSATION_UNSET_MUTED,\n formatConversationForEvent(viewState)\n );\n });\n };\n\n /**\n * Tell the statemanager there is a request to delete the selected messages\n * and run the renderer to show confirm delete messages dialogue.\n *\n * @param {Number} userId User id.\n */\n var requestDeleteSelectedMessages = function(userId) {\n var selectedMessageIds = viewState.selectedMessageIds;\n cancelRequest(userId);\n var newState = StateManager.addPendingDeleteMessagesById(viewState, selectedMessageIds);\n render(newState);\n };\n\n /**\n * Send the repository a request to delete the messages pending deletion. Update the statemanager\n * and publish a message deletion event.\n *\n * @return {Promise} Renderer promise.\n */\n var deleteSelectedMessages = function() {\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:deleteSelectedMessages');\n var messageIds = viewState.pendingDeleteMessageIds;\n var sentMessages = viewState.messages.filter(function(message) {\n // If a message sendState is null then it means it was loaded from the server or if it's\n // set to sent then it means the user has successfully sent it in this page load.\n return messageIds.indexOf(message.id) >= 0 && (message.sendState == 'sent' || message.sendState === null);\n });\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n\n render(newState);\n\n var deleteMessagesPromise = $.Deferred().resolve().promise();\n\n\n if (sentMessages.length) {\n // We only need to send a request to the server if we're trying to delete messages that\n // have successfully been sent.\n var sentMessageIds = sentMessages.map(function(message) {\n return message.id;\n });\n if (newState.deleteMessagesForAllUsers) {\n deleteMessagesPromise = Repository.deleteMessagesForAllUsers(viewState.loggedInUserId, sentMessageIds);\n } else {\n deleteMessagesPromise = Repository.deleteMessages(viewState.loggedInUserId, sentMessageIds);\n }\n }\n\n // Mark that we are deleting content from the conversation to prevent updates of it.\n isDeletingConversationContent = true;\n\n // Stop polling for new messages to the open conversation.\n if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\n }\n\n return deleteMessagesPromise.then(function() {\n var newState = StateManager.removeMessagesById(viewState, messageIds);\n newState = StateManager.removePendingDeleteMessagesById(newState, messageIds);\n newState = StateManager.removeSelectedMessagesById(newState, messageIds);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n newState = StateManager.setDeleteMessagesForAllUsers(newState, false);\n\n var prevLastMessage = viewState.messages[viewState.messages.length - 1];\n var newLastMessage = newState.messages.length ? newState.messages[newState.messages.length - 1] : null;\n\n if (newLastMessage && newLastMessage.id != prevLastMessage.id) {\n var conversation = formatConversationForEvent(newState);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation);\n } else if (!newState.messages.length) {\n PubSub.publish(MessageDrawerEvents.CONVERSATION_DELETED, newState.id);\n }\n\n isDeletingConversationContent = false;\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(Notification.exception);\n };\n\n /**\n * Tell the statemanager there is a request to delete a conversation\n * and run the renderer to show confirm delete conversation dialogue.\n *\n * @param {Number} userId User id of other user.\n */\n var requestDeleteConversation = function(userId) {\n cancelRequest(userId);\n var newState = StateManager.setPendingDeleteConversation(viewState, true);\n render(newState);\n };\n\n /**\n * Send the repository a request to delete a conversation. Update the statemanager\n * and publish a conversation deleted event.\n *\n * @return {Promise} Renderer promise.\n */\n var deleteConversation = function() {\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:markConversationAsRead');\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n render(newState);\n\n // Mark that we are deleting the conversation to prevent updates of it.\n isDeletingConversationContent = true;\n\n // Stop polling for new messages to the open conversation.\n if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\n }\n\n return Repository.deleteConversation(viewState.loggedInUserId, viewState.id)\n .then(function() {\n var newState = StateManager.removeMessages(viewState, viewState.messages);\n newState = StateManager.removeSelectedMessagesById(newState, viewState.selectedMessageIds);\n newState = StateManager.setPendingDeleteConversation(newState, false);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_DELETED, newState.id);\n\n isDeletingConversationContent = false;\n\n return render(newState);\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Tell the statemanager to cancel all pending actions.\n *\n * @param {Number} userId User id.\n */\n var cancelRequest = function(userId) {\n var pendingDeleteMessageIds = viewState.pendingDeleteMessageIds;\n var newState = StateManager.removePendingAddContactsById(viewState, [userId]);\n newState = StateManager.removePendingRemoveContactsById(newState, [userId]);\n newState = StateManager.removePendingUnblockUsersById(newState, [userId]);\n newState = StateManager.removePendingBlockUsersById(newState, [userId]);\n newState = StateManager.removePendingDeleteMessagesById(newState, pendingDeleteMessageIds);\n newState = StateManager.setPendingDeleteConversation(newState, false);\n newState = StateManager.setDeleteMessagesForAllUsers(newState, false);\n render(newState);\n };\n\n /**\n * Accept the contact request from the given user.\n *\n * @param {Number} userId User id of other user.\n * @return {Promise} Renderer promise.\n */\n var acceptContactRequest = function(userId) {\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:acceptContactRequest');\n\n // Search the list of the logged in user's contact requests to find the\n // one from this user.\n var loggedInUserId = viewState.loggedInUserId;\n var requests = viewState.members[userId].contactrequests.filter(function(request) {\n return request.requesteduserid == loggedInUserId;\n });\n var request = requests[0];\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n render(newState);\n\n return Repository.acceptContactRequest(userId, loggedInUserId)\n .then(function(profile) {\n var newState = StateManager.removeContactRequests(viewState, [request]);\n newState = StateManager.addMembers(viewState, [profile]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n return render(newState);\n })\n .then(function() {\n PubSub.publish(MessageDrawerEvents.CONTACT_ADDED, viewState.members[userId]);\n PubSub.publish(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED, request);\n return;\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Decline the contact request from the given user.\n *\n * @param {Number} userId User id of other user.\n * @return {Promise} Renderer promise.\n */\n var declineContactRequest = function(userId) {\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:declineContactRequest');\n\n // Search the list of the logged in user's contact requests to find the\n // one from this user.\n var loggedInUserId = viewState.loggedInUserId;\n var requests = viewState.members[userId].contactrequests.filter(function(request) {\n return request.requesteduserid == loggedInUserId;\n });\n var request = requests[0];\n var newState = StateManager.setLoadingConfirmAction(viewState, true);\n render(newState);\n\n return Repository.declineContactRequest(userId, loggedInUserId)\n .then(function(profile) {\n var newState = StateManager.removeContactRequests(viewState, [request]);\n newState = StateManager.addMembers(viewState, [profile]);\n newState = StateManager.setLoadingConfirmAction(newState, false);\n return render(newState);\n })\n .then(function() {\n PubSub.publish(MessageDrawerEvents.CONTACT_REQUEST_DECLINED, request);\n return;\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Send all of the messages in the buffer to the server to be created. Update the\n * UI with the newly created message information.\n *\n * This function will recursively call itself in order to make sure the buffer is\n * always being processed.\n */\n var processSendMessageBuffer = function() {\n if (isSendingMessage) {\n // We're already sending messages so nothing to do.\n return;\n }\n if (!sendMessageBuffer.length) {\n // No messages waiting to send. Nothing to do.\n return;\n }\n\n var pendingPromise = new Pending('core_message/message_drawer_view_conversation:processSendMessageBuffer');\n\n // Flag that we're processing the queue.\n isSendingMessage = true;\n // Grab all of the messages in the buffer.\n var messagesToSend = sendMessageBuffer.slice();\n // Empty the buffer since we're processing it.\n sendMessageBuffer = [];\n var conversationId = viewState.id;\n var newConversationId = null;\n var messagesText = messagesToSend.map(function(message) {\n return message.text;\n });\n var messageIds = messagesToSend.map(function(message) {\n return message.id;\n });\n var sendMessagePromise = null;\n var newCanDeleteMessagesForAllUsers = null;\n if (!conversationId && (viewState.type != CONVERSATION_TYPES.PUBLIC)) {\n // If it's a new private conversation then we need to use the old\n // web service function to create the conversation.\n var otherUserId = getOtherUserId();\n sendMessagePromise = Repository.sendMessagesToUser(otherUserId, messagesText)\n .then(function(messages) {\n if (messages.length) {\n newConversationId = parseInt(messages[0].conversationid, 10);\n newCanDeleteMessagesForAllUsers = messages[0].candeletemessagesforallusers;\n }\n return messages;\n });\n } else {\n sendMessagePromise = Repository.sendMessagesToConversation(conversationId, messagesText);\n }\n\n sendMessagePromise\n .then(function(messages) {\n var newMessageIds = messages.map(function(message) {\n return message.id;\n });\n var data = [];\n var selectedToRemove = [];\n var selectedToAdd = [];\n\n messagesToSend.forEach(function(oldMessage, index) {\n var newMessage = messages[index];\n // Update messages expects and array of arrays where the first value\n // is the old message to update and the second value is the new values\n // to set.\n data.push([oldMessage, newMessage]);\n\n if (viewState.selectedMessageIds.indexOf(oldMessage.id) >= 0) {\n // If the message was added to the \"selected messages\" list while it was still\n // being sent then we should update it's id in that list now to make sure future\n // actions work.\n selectedToRemove.push(oldMessage.id);\n selectedToAdd.push(newMessage.id);\n }\n });\n var newState = StateManager.updateMessages(viewState, data);\n newState = StateManager.setMessagesSendSuccessById(newState, newMessageIds);\n\n if (selectedToRemove.length) {\n newState = StateManager.removeSelectedMessagesById(newState, selectedToRemove);\n }\n\n if (selectedToAdd.length) {\n newState = StateManager.addSelectedMessagesById(newState, selectedToAdd);\n }\n\n var conversation = formatConversationForEvent(newState);\n\n if (!newState.id) {\n // If this message created the conversation then save the conversation\n // id.\n newState = StateManager.setId(newState, newConversationId);\n conversation.id = newConversationId;\n resetMessagePollTimer(newConversationId);\n PubSub.publish(MessageDrawerEvents.CONVERSATION_CREATED, conversation);\n newState = StateManager.setCanDeleteMessagesForAllUsers(newState, newCanDeleteMessagesForAllUsers);\n }\n\n // Update the UI with the new message values from the server.\n render(newState);\n // Recurse just in case there has been more messages added to the buffer.\n isSendingMessage = false;\n processSendMessageBuffer();\n PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation);\n return;\n })\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(function(e) {\n var errorMessage;\n if (e.message) {\n errorMessage = $.Deferred().resolve(e.message).promise();\n } else {\n errorMessage = Str.get_string('unknownerror', 'core');\n }\n\n var handleFailedMessages = function(errorMessage) {\n // We failed to create messages so remove the old messages from the pending queue\n // and update the UI to indicate that the message failed.\n var newState = StateManager.setMessagesSendFailById(viewState, messageIds, errorMessage);\n render(newState);\n isSendingMessage = false;\n processSendMessageBuffer();\n };\n\n errorMessage.then(handleFailedMessages)\n .then(function(result) {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(function(e) {\n // Hrmm, we can't even load the error messages string! We'll have to\n // hard code something in English here if we still haven't got a message\n // to show.\n var finalError = e.message || 'Something went wrong!';\n handleFailedMessages(finalError);\n });\n });\n };\n\n /**\n * Create a plain version of an HTML text.\n *\n * This texts is used as a message preview while is sent to the server. This way\n * it is possible to prevent self-xss.\n *\n * @param {String} text Text to send.\n * @return {String} The plain text version of the text.\n */\n const previewText = function(text) {\n // Remove all script and styles from text (we don't want it there).\n let plaintext = text.replace(//gi, '');\n plaintext = plaintext.replace(//gi, '');\n // Beautify a bit the output adding some line breaks.\n plaintext = plaintext.replace(/<\\/div>/ig, '\\n');\n plaintext = plaintext.replace(/<\\/li>/ig, '\\n');\n plaintext = plaintext.replace(/
  • /ig, ' * ');\n plaintext = plaintext.replace(/<\\/ul>/ig, '\\n');\n plaintext = plaintext.replace(/<\\/p>/ig, '\\n');\n plaintext = plaintext.replace(/]*>/gi, '\\n');\n // Remove all remaining tags and convert line breaks into html.\n plaintext = plaintext.replace(/<[^>]+>/ig, '');\n plaintext = plaintext.replace(/\\n+/ig, '\\n');\n return plaintext.replace(/\\n/ig, '
    ');\n };\n\n /**\n * Buffers messages to be sent to the server. We use a buffer here to allow the\n * user to freely input messages without blocking the interface for them.\n *\n * Instead we just queue all of their messages up and send them as fast as we can.\n *\n * @param {String} text Text to send.\n */\n var sendMessage = function(text) {\n var id = 'temp' + Date.now();\n // Render a preview version of the message while sending.\n let loadingmessage = {\n id: id,\n useridfrom: viewState.loggedInUserId,\n text: previewText(text),\n timecreated: null\n };\n var newState = StateManager.addMessages(viewState, [loadingmessage]);\n render(newState);\n // Send the real message.\n var message = {\n id: id,\n useridfrom: viewState.loggedInUserId,\n text: text,\n timecreated: null\n };\n sendMessageBuffer.push(message);\n processSendMessageBuffer();\n\n // Remove the unsent message attribute so we can be notified of new unsent messages (see storeUnsentMessage).\n const textArea = document.querySelector(SELECTORS.MESSAGE_TEXT_AREA);\n if (textArea) {\n textArea.removeAttribute('data-unsent-message-viewed');\n }\n };\n\n /**\n * Retry sending a message that failed.\n *\n * @param {Object} message The message to send.\n */\n var retrySendMessage = function(message) {\n var newState = StateManager.setMessagesSendPendingById(viewState, [message.id]);\n render(newState);\n sendMessageBuffer.push(message);\n processSendMessageBuffer();\n };\n\n /**\n * Toggle the selected messages update the statemanager and render the result.\n *\n * @param {Number} messageId The id of the message to be toggled\n */\n var toggleSelectMessage = function(messageId) {\n var newState = viewState;\n\n if (viewState.selectedMessageIds.indexOf(messageId) > -1) {\n newState = StateManager.removeSelectedMessagesById(viewState, [messageId]);\n } else {\n newState = StateManager.addSelectedMessagesById(viewState, [messageId]);\n }\n\n render(newState);\n };\n\n /**\n * Cancel edit mode (selecting the messages).\n */\n var cancelEditMode = function() {\n cancelRequest(getOtherUserId());\n var newState = StateManager.removeSelectedMessagesById(viewState, viewState.selectedMessageIds);\n render(newState);\n };\n\n /**\n * Process the patches in the render buffer one at a time in order until the\n * buffer is empty.\n *\n * @param {Object} header The conversation header container element.\n * @param {Object} body The conversation body container element.\n * @param {Object} footer The conversation footer container element.\n */\n var processRenderBuffer = function(header, body, footer) {\n if (isRendering) {\n return;\n }\n\n if (!renderBuffer.length) {\n return;\n }\n\n isRendering = true;\n var renderable = renderBuffer.shift();\n var renderPromises = renderers.map(function(renderFunc) {\n return renderFunc(renderable.patch);\n });\n\n $.when.apply(null, renderPromises)\n .then(function() {\n isRendering = false;\n renderable.deferred.resolve(true);\n // Keep processing the buffer until it's empty.\n processRenderBuffer(header, body, footer);\n\n return;\n })\n .catch(function(error) {\n isRendering = false;\n renderable.deferred.reject(error);\n Notification.exception(error);\n });\n };\n\n /**\n * Create a function to render the Conversation.\n *\n * @param {Object} header The conversation header container element.\n * @param {Object} body The conversation body container element.\n * @param {Object} footer The conversation footer container element.\n * @param {Bool} isNewConversation Has someone else already initialised a conversation?\n * @return {Promise} Renderer promise.\n */\n var generateRenderFunction = function(header, body, footer, isNewConversation) {\n var rendererFunc = function(patch) {\n return Renderer.render(header, body, footer, patch);\n };\n\n if (!isNewConversation) {\n // Looks like someone got here before us! We'd better update our\n // UI to make sure it matches.\n var initialState = StateManager.buildInitialState(viewState.midnight, viewState.loggedInUserId, viewState.id);\n var syncPatch = Patcher.buildPatch(initialState, viewState);\n rendererFunc(syncPatch);\n }\n\n renderers.push(rendererFunc);\n\n return function(newState) {\n var patch = Patcher.buildPatch(viewState, newState);\n var deferred = $.Deferred();\n\n // Check if the patch has any data. Ignore empty patches.\n if (Object.keys(patch).length) {\n // Add the patch to the render buffer which gets processed in order.\n renderBuffer.push({\n patch: patch,\n deferred: deferred\n });\n } else {\n deferred.resolve(true);\n }\n // This is a great place to add in some console logging if you need\n // to debug something. You can log the current state, the next state,\n // and the generated patch and see exactly what will be updated.\n\n // Optimistically update the state. We're going to assume that the rendering\n // will always succeed. The rendering is asynchronous (annoyingly) so it's buffered\n // but it'll reach eventual consistency with the current state.\n viewState = newState;\n if (newState.id) {\n // Only cache created conversations.\n stateCache[newState.id] = {\n state: newState,\n messagesOffset: getMessagesOffset(),\n loadedAllMessages: hasLoadedAllMessages()\n };\n }\n\n // Start processing the buffer.\n processRenderBuffer(header, body, footer);\n\n return deferred.promise();\n };\n };\n\n /**\n * Create a confirm action function.\n *\n * @param {Function} actionCallback The callback function.\n * @return {Function} Confirm action handler.\n */\n var generateConfirmActionHandler = function(actionCallback) {\n return function(e, data) {\n if (!viewState.loadingConfirmAction) {\n actionCallback(getOtherUserId());\n var newState = StateManager.setLoadingConfirmAction(viewState, false);\n render(newState);\n }\n data.originalEvent.preventDefault();\n };\n };\n\n /**\n * Send message event handler.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleSendMessage = function(e, data) {\n var target = $(e.target);\n var footerContainer = target.closest(SELECTORS.FOOTER_CONTAINER);\n var textArea = footerContainer.find(SELECTORS.MESSAGE_TEXT_AREA);\n var text = textArea.val().trim();\n\n if (text !== '') {\n sendMessage(text);\n textArea.val('');\n textArea.focus();\n }\n\n data.originalEvent.preventDefault();\n };\n\n /**\n * Select message event handler.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleSelectMessage = function(e, data) {\n var selection = window.getSelection();\n var target = $(e.target);\n\n if (selection.toString() != '') {\n // Bail if we're selecting.\n return;\n }\n\n if (target.is('a')) {\n // Clicking on a link in the message so ignore it.\n return;\n }\n\n var element = target.closest(SELECTORS.MESSAGE);\n var messageId = element.attr('data-message-id');\n\n toggleSelectMessage(messageId);\n\n data.originalEvent.preventDefault();\n };\n\n /**\n * Handle retry sending of message.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleRetrySendMessage = function(e, data) {\n var target = $(e.target);\n var element = target.closest(SELECTORS.MESSAGE);\n var messageId = element.attr('data-message-id');\n var messages = viewState.messages.filter(function(message) {\n return message.id == messageId;\n });\n var message = messages.length ? messages[0] : null;\n\n if (message) {\n retrySendMessage(message);\n }\n\n data.originalEvent.preventDefault();\n data.originalEvent.stopPropagation();\n e.stopPropagation();\n };\n\n /**\n * Cancel edit mode event handler.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleCancelEditMode = function(e, data) {\n cancelEditMode();\n data.originalEvent.preventDefault();\n };\n\n /**\n * Show the view contact page.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @return {Function} View contact handler.\n */\n var generateHandleViewContact = function(namespace) {\n return function(e, data) {\n var otherUserId = getOtherUserId();\n var otherUser = viewState.members[otherUserId];\n MessageDrawerRouter.go(namespace, MessageDrawerRoutes.VIEW_CONTACT, otherUser);\n data.originalEvent.preventDefault();\n };\n };\n\n /**\n * Set this conversation as a favourite.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleSetFavourite = function(e, data) {\n setFavourite().catch(Notification.exception);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Unset this conversation as a favourite.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleUnsetFavourite = function(e, data) {\n unsetFavourite().catch(Notification.exception);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Show the view group info page.\n * Set this conversation as muted.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleSetMuted = function(e, data) {\n setMuted().catch(Notification.exception);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Unset this conversation as muted.\n *\n * @param {Object} e Element this event handler is called on.\n * @param {Object} data Data for this event.\n */\n var handleUnsetMuted = function(e, data) {\n unsetMuted().catch(Notification.exception);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Handle clicking on the checkbox that toggles deleting messages for\n * all users.\n *\n * @param {Object} e Element this event handler is called on.\n */\n var handleDeleteMessagesForAllUsersToggle = function(e) {\n var newValue = $(e.target).prop('checked');\n var newState = StateManager.setDeleteMessagesForAllUsers(viewState, newValue);\n render(newState);\n };\n\n /**\n * Show the view contact page.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @return {Function} View group info handler.\n */\n var generateHandleViewGroupInfo = function(namespace) {\n return function(e, data) {\n MessageDrawerRouter.go(\n namespace,\n MessageDrawerRoutes.VIEW_GROUP_INFO,\n {\n id: viewState.id,\n name: viewState.name,\n subname: viewState.subname,\n imageUrl: viewState.imageUrl,\n totalMemberCount: viewState.totalMemberCount\n },\n viewState.loggedInUserId\n );\n data.originalEvent.preventDefault();\n };\n };\n\n /**\n * Handle clicking on the emoji toggle button.\n *\n * @param {Object} e The event\n * @param {Object} data The custom interaction event data\n */\n var handleToggleEmojiPicker = function(e, data) {\n var newState = StateManager.setShowEmojiPicker(viewState, !viewState.showEmojiPicker);\n render(newState);\n data.originalEvent.preventDefault();\n };\n\n /**\n * Handle clicking outside the emoji picker to close it.\n *\n * @param {Object} e The event\n */\n var handleCloseEmojiPicker = function(e) {\n var target = $(e.target);\n\n if (\n viewState.showEmojiPicker &&\n !target.closest(SELECTORS.EMOJI_PICKER_CONTAINER).length &&\n !target.closest(SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON).length\n ) {\n var newState = StateManager.setShowEmojiPicker(viewState, false);\n render(newState);\n }\n };\n\n /**\n * Listen to, and handle events for conversations.\n *\n * @param {string} namespace The route namespace.\n * @param {Object} header Conversation header container element.\n * @param {Object} body Conversation body container element.\n * @param {Object} footer Conversation footer container element.\n */\n var registerEventListeners = function(namespace, header, body, footer) {\n var isLoadingMoreMessages = false;\n var messagesContainer = getMessagesContainer(body);\n var emojiPickerElement = footer.find(SELECTORS.EMOJI_PICKER);\n var emojiAutoCompleteContainer = footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER);\n var messageTextArea = footer.find(SELECTORS.MESSAGE_TEXT_AREA);\n var headerActivateHandlers = [\n [SELECTORS.ACTION_REQUEST_BLOCK, generateConfirmActionHandler(requestBlockUser)],\n [SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],\n [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],\n [SELECTORS.ACTION_REQUEST_REMOVE_CONTACT, generateConfirmActionHandler(requestRemoveContact)],\n [SELECTORS.ACTION_REQUEST_DELETE_CONVERSATION, generateConfirmActionHandler(requestDeleteConversation)],\n [SELECTORS.ACTION_CANCEL_EDIT_MODE, handleCancelEditMode],\n [SELECTORS.ACTION_VIEW_CONTACT, generateHandleViewContact(namespace)],\n [SELECTORS.ACTION_VIEW_GROUP_INFO, generateHandleViewGroupInfo(namespace)],\n [SELECTORS.ACTION_CONFIRM_FAVOURITE, handleSetFavourite],\n [SELECTORS.ACTION_CONFIRM_MUTE, handleSetMuted],\n [SELECTORS.ACTION_CONFIRM_UNFAVOURITE, handleUnsetFavourite],\n [SELECTORS.ACTION_CONFIRM_UNMUTE, handleUnsetMuted]\n ];\n var bodyActivateHandlers = [\n [SELECTORS.ACTION_CANCEL_CONFIRM, generateConfirmActionHandler(cancelRequest)],\n [SELECTORS.ACTION_CONFIRM_BLOCK, generateConfirmActionHandler(blockUser)],\n [SELECTORS.ACTION_CONFIRM_UNBLOCK, generateConfirmActionHandler(unblockUser)],\n [SELECTORS.ACTION_CONFIRM_ADD_CONTACT, generateConfirmActionHandler(addContact)],\n [SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT, generateConfirmActionHandler(removeContact)],\n [SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES, generateConfirmActionHandler(deleteSelectedMessages)],\n [SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION, generateConfirmActionHandler(deleteConversation)],\n [SELECTORS.ACTION_OKAY_CONFIRM, generateConfirmActionHandler(cancelRequest)],\n [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],\n [SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST, generateConfirmActionHandler(acceptContactRequest)],\n [SELECTORS.ACTION_DECLINE_CONTACT_REQUEST, generateConfirmActionHandler(declineContactRequest)],\n [SELECTORS.MESSAGE, handleSelectMessage],\n [SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE, handleDeleteMessagesForAllUsersToggle],\n [SELECTORS.RETRY_SEND, handleRetrySendMessage]\n ];\n var footerActivateHandlers = [\n [SELECTORS.SEND_MESSAGE_BUTTON, handleSendMessage],\n [SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON, handleToggleEmojiPicker],\n [SELECTORS.ACTION_REQUEST_DELETE_SELECTED_MESSAGES, generateConfirmActionHandler(requestDeleteSelectedMessages)],\n [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],\n [SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],\n ];\n\n AutoRows.init(footer);\n\n if (emojiAutoCompleteContainer.length) {\n initialiseEmojiAutoComplete(\n emojiAutoCompleteContainer[0],\n messageTextArea[0],\n function(hasSuggestions) {\n var newState = StateManager.setShowEmojiAutoComplete(viewState, hasSuggestions);\n render(newState);\n },\n function(emoji) {\n var newState = StateManager.setShowEmojiAutoComplete(viewState, false);\n render(newState);\n\n messageTextArea.focus();\n var cursorPos = messageTextArea.prop('selectionStart');\n var currentText = messageTextArea.val();\n var textBefore = currentText.substring(0, cursorPos).replace(/\\S*$/, '');\n var textAfter = currentText.substring(cursorPos).replace(/^\\S*/, '');\n\n messageTextArea.val(textBefore + emoji + textAfter);\n // Set the cursor position to after the inserted emoji.\n messageTextArea.prop('selectionStart', textBefore.length + emoji.length);\n messageTextArea.prop('selectionEnd', textBefore.length + emoji.length);\n }\n );\n }\n\n if (emojiPickerElement.length) {\n initialiseEmojiPicker(emojiPickerElement[0], function(emoji) {\n var newState = StateManager.setShowEmojiPicker(viewState, !viewState.showEmojiPicker);\n render(newState);\n\n messageTextArea.focus();\n var cursorPos = messageTextArea.prop('selectionStart');\n var currentText = messageTextArea.val();\n var textBefore = currentText.substring(0, cursorPos);\n var textAfter = currentText.substring(cursorPos, currentText.length);\n\n messageTextArea.val(textBefore + emoji + textAfter);\n // Set the cursor position to after the inserted emoji.\n messageTextArea.prop('selectionStart', cursorPos + emoji.length);\n messageTextArea.prop('selectionEnd', cursorPos + emoji.length);\n });\n }\n\n CustomEvents.define(header, [\n CustomEvents.events.activate\n ]);\n CustomEvents.define(body, [\n CustomEvents.events.activate\n ]);\n CustomEvents.define(footer, [\n CustomEvents.events.activate,\n CustomEvents.events.enter,\n CustomEvents.events.escape\n ]);\n CustomEvents.define(messagesContainer, [\n CustomEvents.events.scrollTop,\n CustomEvents.events.scrollLock\n ]);\n\n messagesContainer.on(CustomEvents.events.scrollTop, function(e, data) {\n var hasMembers = Object.keys(viewState.members).length > 1;\n\n if (!isResetting && !isLoadingMoreMessages && !hasLoadedAllMessages() && hasMembers) {\n isLoadingMoreMessages = true;\n var newState = StateManager.setLoadingMessages(viewState, true);\n render(newState);\n\n loadMessages(viewState.id, LOAD_MESSAGE_LIMIT, getMessagesOffset(), NEWEST_FIRST, [])\n .then(function() {\n isLoadingMoreMessages = false;\n setMessagesOffset(getMessagesOffset() + LOAD_MESSAGE_LIMIT);\n return;\n })\n .catch(function(error) {\n isLoadingMoreMessages = false;\n Notification.exception(error);\n });\n }\n\n data.originalEvent.preventDefault();\n });\n\n headerActivateHandlers.forEach(function(handler) {\n var selector = handler[0];\n var handlerFunction = handler[1];\n header.on(CustomEvents.events.activate, selector, handlerFunction);\n });\n\n bodyActivateHandlers.forEach(function(handler) {\n var selector = handler[0];\n var handlerFunction = handler[1];\n body.on(CustomEvents.events.activate, selector, handlerFunction);\n });\n\n footerActivateHandlers.forEach(function(handler) {\n var selector = handler[0];\n var handlerFunction = handler[1];\n footer.on(CustomEvents.events.activate, selector, handlerFunction);\n });\n\n footer.on(CustomEvents.events.enter, SELECTORS.MESSAGE_TEXT_AREA, function(e, data) {\n var enterToSend = footer.attr('data-enter-to-send');\n if (enterToSend && enterToSend != 'false' && enterToSend != '0') {\n handleSendMessage(e, data);\n }\n });\n\n footer.on(CustomEvents.events.escape, SELECTORS.EMOJI_PICKER_CONTAINER, handleToggleEmojiPicker);\n $(document.body).on('click', handleCloseEmojiPicker);\n\n PubSub.subscribe(MessageDrawerEvents.ROUTE_CHANGED, function(newRouteData) {\n if (newMessagesPollTimer) {\n if (newRouteData.route != MessageDrawerRoutes.VIEW_CONVERSATION) {\n newMessagesPollTimer.stop();\n }\n }\n });\n };\n\n /**\n * Reset the timer that polls for new messages.\n *\n * @param {Number} conversationId The conversation id\n */\n var resetMessagePollTimer = function(conversationId) {\n if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\n }\n\n newMessagesPollTimer = new BackOffTimer(\n getLoadNewMessagesCallback(conversationId, NEWEST_FIRST),\n BackOffTimer.getIncrementalCallback(\n viewState.messagePollMin * MILLISECONDS_IN_SEC,\n MILLISECONDS_IN_SEC,\n viewState.messagePollMax * MILLISECONDS_IN_SEC,\n viewState.messagePollAfterMax * MILLISECONDS_IN_SEC\n )\n );\n\n newMessagesPollTimer.start();\n };\n\n /**\n * Reset the state to the initial state and render the UI.\n *\n * @param {Object} body Conversation body container element.\n * @param {Number|null} conversationId The conversation id.\n * @param {Object} loggedInUserProfile The logged in user's profile.\n */\n var resetState = function(body, conversationId, loggedInUserProfile) {\n // Reset all of the states back to the beginning if we're loading a new\n // conversation.\n if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\n }\n loadedAllMessages = false;\n messagesOffset = 0;\n newMessagesPollTimer = null;\n isRendering = false;\n renderBuffer = [];\n isResetting = true;\n isSendingMessage = false;\n isDeletingConversationContent = false;\n sendMessageBuffer = [];\n\n var loggedInUserId = loggedInUserProfile.id;\n var midnight = parseInt(body.attr('data-midnight'), 10);\n var messagePollMin = parseInt(body.attr('data-message-poll-min'), 10);\n var messagePollMax = parseInt(body.attr('data-message-poll-max'), 10);\n var messagePollAfterMax = parseInt(body.attr('data-message-poll-after-max'), 10);\n var initialState = StateManager.buildInitialState(\n midnight,\n loggedInUserId,\n conversationId,\n messagePollMin,\n messagePollMax,\n messagePollAfterMax\n );\n\n if (!viewState) {\n viewState = initialState;\n }\n\n render(initialState);\n };\n\n /**\n * Load a new empty private conversation between two users or self-conversation.\n *\n * @param {Object} body Conversation body container element.\n * @param {Object} loggedInUserProfile The logged in user's profile.\n * @param {Int} otherUserId The other user's id.\n * @return {Promise} Renderer promise.\n */\n var resetNoConversation = function(body, loggedInUserProfile, otherUserId) {\n // Always reset the state back to the initial state so that the\n // state manager and patcher can work correctly.\n resetState(body, null, loggedInUserProfile);\n\n var resetNoConversationPromise = null;\n\n if (loggedInUserProfile.id != otherUserId) {\n // Private conversation between two different users.\n resetNoConversationPromise = Repository.getConversationBetweenUsers(\n loggedInUserProfile.id,\n otherUserId,\n true,\n true,\n 0,\n 0,\n LOAD_MESSAGE_LIMIT,\n 0,\n NEWEST_FIRST\n );\n } else {\n // Self conversation.\n resetNoConversationPromise = Repository.getSelfConversation(\n loggedInUserProfile.id,\n LOAD_MESSAGE_LIMIT,\n 0,\n NEWEST_FIRST\n );\n }\n\n return resetNoConversationPromise.then(function(conversation) {\n // Looks like we have a conversation after all! Let's use that.\n return resetByConversation(body, conversation, loggedInUserProfile);\n })\n .catch(function() {\n // Can't find a conversation. Oh well. Just load up a blank one.\n return loadEmptyPrivateConversation(loggedInUserProfile, otherUserId);\n });\n };\n\n /**\n * Load new messages into the conversation based on a time interval.\n *\n * @param {Object} body Conversation body container element.\n * @param {Number} conversationId The conversation id.\n * @param {Object} loggedInUserProfile The logged in user's profile.\n * @return {Promise} Renderer promise.\n */\n var resetById = function(body, conversationId, loggedInUserProfile) {\n var cache = null;\n if (conversationId in stateCache) {\n cache = stateCache[conversationId];\n }\n\n // Always reset the state back to the initial state so that the\n // state manager and patcher can work correctly.\n resetState(body, conversationId, loggedInUserProfile);\n\n var promise = $.Deferred().resolve({}).promise();\n if (cache) {\n // We've seen this conversation before so there is no need to\n // send any network requests.\n var newState = cache.state;\n // Reset some loading states just in case they were left weirdly.\n newState = StateManager.setLoadingMessages(newState, false);\n newState = StateManager.setLoadingMembers(newState, false);\n setMessagesOffset(cache.messagesOffset);\n setLoadedAllMessages(cache.loadedAllMessages);\n render(newState);\n } else {\n promise = loadNewConversation(\n conversationId,\n loggedInUserProfile,\n LOAD_MESSAGE_LIMIT,\n 0,\n NEWEST_FIRST\n );\n }\n\n return promise.then(function() {\n return resetMessagePollTimer(conversationId);\n });\n };\n\n /**\n * Load new messages into the conversation based on a time interval.\n *\n * @param {Object} body Conversation body container element.\n * @param {Object} conversation The conversation.\n * @param {Object} loggedInUserProfile The logged in user's profile.\n * @return {Promise} Renderer promise.\n */\n var resetByConversation = function(body, conversation, loggedInUserProfile) {\n var cache = null;\n if (conversation.id in stateCache) {\n cache = stateCache[conversation.id];\n }\n\n // Always reset the state back to the initial state so that the\n // state manager and patcher can work correctly.\n resetState(body, conversation.id, loggedInUserProfile);\n\n var promise = $.Deferred().resolve({}).promise();\n if (cache) {\n // We've seen this conversation before so there is no need to\n // send any network requests.\n var newState = cache.state;\n // Reset some loading states just in case they were left weirdly.\n newState = StateManager.setLoadingMessages(newState, false);\n newState = StateManager.setLoadingMembers(newState, false);\n setMessagesOffset(cache.messagesOffset);\n setLoadedAllMessages(cache.loadedAllMessages);\n render(newState);\n } else {\n promise = loadExistingConversation(\n conversation,\n loggedInUserProfile,\n LOAD_MESSAGE_LIMIT,\n NEWEST_FIRST\n );\n }\n\n return promise.then(function() {\n return resetMessagePollTimer(conversation.id);\n });\n };\n\n /**\n * Setup the conversation page. This is a rather complex function because there are a\n * few combinations of arguments that can be provided to this function to show the\n * conversation.\n *\n * There are:\n * 1.) A conversation object with no action or other user id (e.g. from the overview page)\n * 2.) A conversation id with no action or other user id (e.g. from the contacts page)\n * 3.) No conversation/id with an action and other other user id. (e.g. from contact page)\n *\n * @param {string} namespace The route namespace.\n * @param {Object} header Conversation header container element.\n * @param {Object} body Conversation body container element.\n * @param {Object} footer Conversation footer container element.\n * @param {Object|Number|null} conversationOrId Conversation or id or null\n * @param {String} action An action to take on the conversation\n * @param {Number} otherUserId The other user id for a private conversation\n * @return {Object} jQuery promise\n */\n var show = function(namespace, header, body, footer, conversationOrId, action, otherUserId) {\n var conversation = null;\n var conversationId = null;\n\n // Check what we were given to identify the conversation.\n if (conversationOrId && conversationOrId !== null && typeof conversationOrId == 'object') {\n conversation = conversationOrId;\n conversationId = parseInt(conversation.id, 10);\n } else {\n conversation = null;\n conversationId = parseInt(conversationOrId, 10);\n conversationId = isNaN(conversationId) ? null : conversationId;\n }\n\n if (!conversationId && action && otherUserId) {\n // If we didn't get a conversation id got a user id then let's see if we've\n // previously loaded a private conversation with this user.\n conversationId = getCachedPrivateConversationIdFromUserId(otherUserId);\n }\n\n // Set attributes to aid in the restoration of unsent messages.\n setConversationAttributes(footer, conversationId, otherUserId);\n\n // This is a new conversation if:\n // 1. We don't already have a state\n // 2. The given conversation doesn't match the one currently loaded\n // 3. We have a view state without a conversation id and we weren't given one\n // but we were given a different other user id. This happens when the user\n // goes from viewing a user that they haven't yet initialised a conversation\n // with to viewing a different user that they also haven't initialised a\n // conversation with.\n var isNewConversation = !viewState || (viewState.id != conversationId) || (otherUserId && otherUserId != getOtherUserId());\n\n if (!body.attr('data-init')) {\n // Generate the render function to bind the header, body, and footer\n // elements to it so that we don't need to pass them around this module.\n render = generateRenderFunction(header, body, footer, isNewConversation);\n registerEventListeners(namespace, header, body, footer);\n body.attr('data-init', true);\n }\n\n if (isNewConversation) {\n var renderPromise = null;\n var loggedInUserProfile = getLoggedInUserProfile(body);\n\n if (conversation) {\n renderPromise = resetByConversation(body, conversation, loggedInUserProfile, otherUserId);\n } else if (conversationId) {\n renderPromise = resetById(body, conversationId, loggedInUserProfile, otherUserId);\n } else {\n renderPromise = resetNoConversation(body, loggedInUserProfile, otherUserId);\n }\n\n return renderPromise\n .then(function() {\n isResetting = false;\n // Focus the first element that can receieve it in the header.\n header.find(Constants.SELECTORS.CAN_RECEIVE_FOCUS).first().focus();\n return;\n })\n .catch(function(error) {\n isResetting = false;\n Notification.exception(error);\n });\n }\n\n // We're not loading a new conversation so we should reset the poll timer to try to load\n // new messages.\n resetMessagePollTimer(conversationId);\n\n if (viewState.type == CONVERSATION_TYPES.PRIVATE && action) {\n // There are special actions that the user can perform in a private (aka 1-to-1)\n // conversation.\n var currentOtherUserId = getOtherUserId();\n\n switch (action) {\n case 'block':\n return requestBlockUser(currentOtherUserId);\n case 'unblock':\n return requestUnblockUser(currentOtherUserId);\n case 'add-contact':\n return requestAddContact(currentOtherUserId);\n case 'remove-contact':\n return requestRemoveContact(currentOtherUserId);\n }\n }\n\n // Final fallback to return a promise if we didn't need to do anything.\n return $.Deferred().resolve().promise();\n };\n\n /**\n * String describing this page used for aria-labels.\n *\n * @return {Object} jQuery promise\n */\n var description = function() {\n return Str.get_string('messagedrawerviewconversation', 'core_message', viewState.name);\n };\n\n /**\n * Set some attributes that will help reboot a conversation.\n *\n * These attributes aid in the storage and retrieval of unsent messages.\n * When a conversationid is not present, otheruserid can be used to create a conversation.\n *\n * @param {Object} element The element to target.\n * @param {Number|null} conversationId The conversationid.\n * @param {Number|null} otherUserId The otheruserid.\n */\n const setConversationAttributes = function(element, conversationId, otherUserId) {\n element.removeAttr('data-conversation-id');\n element.removeAttr('data-other-user-id');\n if (conversationId) {\n element.attr('data-conversation-id', conversationId);\n } else if (otherUserId) {\n element.attr('data-other-user-id', otherUserId);\n }\n };\n\n return {\n show: show,\n description: description\n };\n});\n"],"names":["define","$","AutoRows","BackOffTimer","CustomEvents","Notification","Pending","PubSub","Str","Repository","MessageDrawerEvents","Constants","Patcher","Renderer","StateManager","MessageDrawerRouter","MessageDrawerRoutes","initialiseEmojiAutoComplete","initialiseEmojiPicker","stateCache","viewState","loadedAllMessages","messagesOffset","newMessagesPollTimer","isRendering","renderBuffer","isResetting","isSendingMessage","isDeletingConversationContent","sendMessageBuffer","render","renderers","NEWEST_FIRST","NEWEST_MESSAGES_FIRST","LOAD_MESSAGE_LIMIT","MILLISECONDS_IN_SEC","SELECTORS","CONVERSATION_TYPES","getOtherUserId","type","PUBLIC","loggedInUserId","SELF","otherUserIds","Object","keys","members","filter","userId","length","getMessagesOffset","setMessagesOffset","value","id","hasLoadedAllMessages","setLoadedAllMessages","formatConversationForEvent","state","name","subname","imageUrl","isFavourite","isMuted","totalMemberCount","messages","map","message","extend","formattedMember","contactrequests","request","updateStateFromConversation","conversation","otherUser","PRIVATE","otherUsers","member","imageurl","fullname","profileimageurl","newState","addMembers","setName","setSubname","setType","setImageUrl","setTotalMemberCount","membercount","setIsFavourite","isfavourite","setIsMuted","ismuted","addMessages","setCanDeleteMessagesForAllUsers","candeletemessagesforallusers","setCanSendMessageToConversation","cansendmessagetoconversation","loadMessages","conversationId","limit","offset","newestFirst","ignoreList","timeFrom","getMessages","then","result","indexOf","parseInt","slice","membersToAdd","setLoadingMessages","catch","error","markConversationAsRead","pendingPromise","markAllConversationMessagesAsRead","markMessagesAsRead","publish","CONVERSATION_READ","resolve","requestBlockUser","cancelRequest","addPendingBlockUsersById","blockUser","setLoadingConfirmAction","profile","removePendingBlockUsersById","CONTACT_BLOCKED","requestUnblockUser","addPendingUnblockUsersById","unblockUser","removePendingUnblockUsersById","CONTACT_UNBLOCKED","requestRemoveContact","addPendingRemoveContactsById","removeContact","deleteContacts","profiles","removePendingRemoveContactsById","CONTACT_REMOVED","requestAddContact","addPendingAddContactsById","addContact","createContactRequest","response","Error","warnings","removePendingAddContactsById","addContactRequests","requestDeleteSelectedMessages","selectedMessageIds","addPendingDeleteMessagesById","deleteSelectedMessages","messageIds","pendingDeleteMessageIds","sentMessages","sendState","deleteMessagesPromise","Deferred","promise","sentMessageIds","deleteMessagesForAllUsers","deleteMessages","stop","removeMessagesById","removePendingDeleteMessagesById","removeSelectedMessagesById","setDeleteMessagesForAllUsers","prevLastMessage","newLastMessage","CONVERSATION_NEW_LAST_MESSAGE","CONVERSATION_DELETED","exception","requestDeleteConversation","setPendingDeleteConversation","deleteConversation","removeMessages","acceptContactRequest","requests","requesteduserid","removeContactRequests","CONTACT_ADDED","CONTACT_REQUEST_ACCEPTED","declineContactRequest","CONTACT_REQUEST_DECLINED","processSendMessageBuffer","messagesToSend","newConversationId","messagesText","text","sendMessagePromise","newCanDeleteMessagesForAllUsers","sendMessagesToConversation","otherUserId","sendMessagesToUser","conversationid","newMessageIds","data","selectedToRemove","selectedToAdd","forEach","oldMessage","index","newMessage","push","updateMessages","setMessagesSendSuccessById","addSelectedMessagesById","setId","resetMessagePollTimer","CONVERSATION_CREATED","e","errorMessage","get_string","handleFailedMessages","setMessagesSendFailById","finalError","previewText","plaintext","replace","processRenderBuffer","header","body","footer","renderable","shift","renderPromises","renderFunc","patch","when","apply","deferred","reject","generateConfirmActionHandler","actionCallback","loadingConfirmAction","originalEvent","preventDefault","handleSendMessage","textArea","target","closest","FOOTER_CONTAINER","find","MESSAGE_TEXT_AREA","val","trim","Date","now","loadingmessage","useridfrom","timecreated","document","querySelector","removeAttribute","sendMessage","focus","handleSelectMessage","selection","window","getSelection","toString","is","messageId","toggleSelectMessage","MESSAGE","attr","handleRetrySendMessage","setMessagesSendPendingById","retrySendMessage","stopPropagation","handleCancelEditMode","cancelEditMode","generateHandleViewContact","namespace","go","VIEW_CONTACT","handleSetFavourite","setFavouriteConversations","CONVERSATION_SET_FAVOURITE","handleUnsetFavourite","unsetFavouriteConversations","CONVERSATION_UNSET_FAVOURITE","handleSetMuted","setMutedConversations","CONVERSATION_SET_MUTED","handleUnsetMuted","unsetMutedConversations","CONVERSATION_UNSET_MUTED","handleDeleteMessagesForAllUsersToggle","newValue","prop","generateHandleViewGroupInfo","VIEW_GROUP_INFO","handleToggleEmojiPicker","setShowEmojiPicker","showEmojiPicker","handleCloseEmojiPicker","EMOJI_PICKER_CONTAINER","TOGGLE_EMOJI_PICKER_BUTTON","registerEventListeners","isLoadingMoreMessages","messagesContainer","MESSAGES_CONTAINER","getMessagesContainer","emojiPickerElement","EMOJI_PICKER","emojiAutoCompleteContainer","EMOJI_AUTO_COMPLETE_CONTAINER","messageTextArea","headerActivateHandlers","ACTION_REQUEST_BLOCK","ACTION_REQUEST_UNBLOCK","ACTION_REQUEST_ADD_CONTACT","ACTION_REQUEST_REMOVE_CONTACT","ACTION_REQUEST_DELETE_CONVERSATION","ACTION_CANCEL_EDIT_MODE","ACTION_VIEW_CONTACT","ACTION_VIEW_GROUP_INFO","ACTION_CONFIRM_FAVOURITE","ACTION_CONFIRM_MUTE","ACTION_CONFIRM_UNFAVOURITE","ACTION_CONFIRM_UNMUTE","bodyActivateHandlers","ACTION_CANCEL_CONFIRM","ACTION_CONFIRM_BLOCK","ACTION_CONFIRM_UNBLOCK","ACTION_CONFIRM_ADD_CONTACT","ACTION_CONFIRM_REMOVE_CONTACT","ACTION_CONFIRM_DELETE_SELECTED_MESSAGES","ACTION_CONFIRM_DELETE_CONVERSATION","ACTION_OKAY_CONFIRM","ACTION_ACCEPT_CONTACT_REQUEST","ACTION_DECLINE_CONTACT_REQUEST","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE","RETRY_SEND","footerActivateHandlers","SEND_MESSAGE_BUTTON","ACTION_REQUEST_DELETE_SELECTED_MESSAGES","init","hasSuggestions","setShowEmojiAutoComplete","emoji","cursorPos","currentText","textBefore","substring","textAfter","events","activate","enter","escape","scrollTop","scrollLock","on","hasMembers","handler","selector","handlerFunction","enterToSend","subscribe","ROUTE_CHANGED","newRouteData","route","VIEW_CONVERSATION","mostRecentMessage","lastTimeCreated","timeCreated","ignoreMessageIds","i","restart","getLoadNewMessagesCallback","getIncrementalCallback","messagePollMin","messagePollMax","messagePollAfterMax","start","resetState","loggedInUserProfile","midnight","initialState","buildInitialState","resetNoConversation","getConversationBetweenUsers","getSelfConversation","resetByConversation","conversationType","setLoadingMembers","getMemberInfo","loadEmptyPrivateConversation","resetById","cache","messageLimit","messageOffset","getConversation","concat","loadNewConversation","messageCount","hasLoadedEnoughMessages","loadExistingConversation","setConversationAttributes","element","removeAttr","show","conversationOrId","action","isNaN","reduce","carry","isNewConversation","rendererFunc","buildPatch","generateRenderFunction","profileimageurlsmall","isonline","showonlinestatus","isblocked","iscontact","isdeleted","canmessage","canmessageevenifblocked","requirescontact","cancreatecontact","getLoggedInUserProfile","CAN_RECEIVE_FOCUS","first","currentOtherUserId","description"],"mappings":"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"} \ No newline at end of file diff --git a/public/message/amd/build/message_drawer_view_conversation_constants.min.js b/public/message/amd/build/message_drawer_view_conversation_constants.min.js index 09559fc9ae0..634ed114df5 100644 --- a/public/message/amd/build/message_drawer_view_conversation_constants.min.js +++ b/public/message/amd/build/message_drawer_view_conversation_constants.min.js @@ -5,6 +5,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("core_message/message_drawer_view_conversation_constants",[],(function(){return{SELECTORS:{ACTION_ACCEPT_CONTACT_REQUEST:'[data-action="accept-contact-request"]',ACTION_CANCEL_CONFIRM:'[data-action="cancel-confirm"]',ACTION_OKAY_CONFIRM:'[data-action="okay-confirm"]',ACTION_CANCEL_EDIT_MODE:'[data-action="cancel-edit-mode"]',ACTION_CONFIRM_ADD_CONTACT:'[data-action="confirm-add-contact"]',ACTION_CONFIRM_BLOCK:'[data-action="confirm-block"]',ACTION_CONFIRM_DELETE_SELECTED_MESSAGES:'[data-action="confirm-delete-selected-messages"]',ACTION_CONFIRM_DELETE_CONVERSATION:'[data-action="confirm-delete-conversation"]',ACTION_CONFIRM_FAVOURITE:'[data-action="confirm-favourite"]',ACTION_CONFIRM_MUTE:'[data-action="confirm-mute"]',ACTION_CONFIRM_UNFAVOURITE:'[data-action="confirm-unfavourite"]',ACTION_CONFIRM_REMOVE_CONTACT:'[data-action="confirm-remove-contact"]',ACTION_CONFIRM_UNBLOCK:'[data-action="confirm-unblock"]',ACTION_CONFIRM_UNMUTE:'[data-action="confirm-unmute"]',ACTION_DECLINE_CONTACT_REQUEST:'[data-action="decline-contact-request"]',ACTION_REQUEST_ADD_CONTACT:'[data-action="request-add-contact"]',ACTION_REQUEST_BLOCK:'[data-action="request-block"]',ACTION_REQUEST_DELETE_CONVERSATION:'[data-action="request-delete-conversation"]',ACTION_REQUEST_DELETE_SELECTED_MESSAGES:'[data-action="delete-selected-messages"]',ACTION_REQUEST_REMOVE_CONTACT:'[data-action="request-remove-contact"]',ACTION_REQUEST_UNBLOCK:'[data-action="request-unblock"]',ACTION_VIEW_CONTACT:'[data-action="view-contact"]',ACTION_VIEW_GROUP_INFO:'[data-action="view-group-info"]',CAN_RECEIVE_FOCUS:'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',CONFIRM_DIALOGUE:'[data-region="confirm-dialogue"]',CONFIRM_DIALOGUE_BUTTON_TEXT:'[data-region="dialogue-button-text"]',CONFIRM_DIALOGUE_CANCEL_BUTTON:'[data-action="cancel-confirm"]',CONFIRM_DIALOGUE_CONTAINER:'[data-region="confirm-dialogue-container"]',CONFIRM_DIALOGUE_HEADER:'[data-region="dialogue-header"]',CONFIRM_DIALOGUE_OKAY_BUTTON:'[data-action="okay-confirm"]',CONFIRM_DIALOGUE_TEXT:'[data-region="dialogue-text"]',CONTACT_REQUEST_SENT_MESSAGE_CONTAINER:'[data-region="contact-request-sent-message-container"]',CONTENT_PLACEHOLDER_CONTAINER:'[data-region="content-placeholder"]',CONTENT_CONTAINER:'[data-region="content-container"]',CONTENT_MESSAGES_CONTAINER:'[data-region="content-message-container"]',CONTENT_MESSAGES_FOOTER_CONTAINER:'[data-region="content-messages-footer-container"]',CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER:'[data-region="content-messages-footer-edit-mode-container"]',CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER:'[data-region="content-messages-footer-require-contact-container"]',CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER:'[data-region="content-messages-footer-require-unblock-container"]',CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER:'[data-region="content-messages-footer-unable-to-message"]',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"]',EMOJI_AUTO_COMPLETE_CONTAINER:'[data-region="emoji-auto-complete-container"]',EMOJI_PICKER_CONTAINER:'[data-region="emoji-picker-container"]',EMOJI_PICKER:'[data-region="emoji-picker"]',EMOJI_PICKER_SEARCH_INPUT:'[data-region="search-input"]',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"]',HEADER_EDIT_MODE:'[data-region="header-edit-mode"]',HEADER_PLACEHOLDER_CONTAINER:'[data-region="header-placeholder"]',LOADING_ICON_CONTAINER:'[data-region="loading-icon-container"]',MESSAGE:'[data-region="message"]',MESSAGE_NOT_SELECTED:'[data-region="message"][aria-checked="false"]',MESSAGE_NOT_SELECTED_ICON:'[data-region="not-selected-icon"]',MESSAGE_SELECTED_ICON:'[data-region="selected-icon"]',MESSAGES:'[data-region="content-message-container"]',MESSAGES_CONTAINER:'[data-region="content-message-container"]',MESSAGES_SELECTED_COUNT:'[data-region="message-selected-court"]',MESSAGE_TEXT_AREA:'[data-region="send-message-txt"]',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"]',TOGGLE_EMOJI_PICKER_BUTTON:'[data-action="toggle-emoji-picker"]'},TEMPLATES:{HEADER_PRIVATE:"core_message/message_drawer_view_conversation_header_content_type_private",HEADER_PRIVATE_NO_CONTROLS:"core_message/message_drawer_view_conversation_header_content_type_private_no_controls",HEADER_PUBLIC:"core_message/message_drawer_view_conversation_header_content_type_public",HEADER_SELF:"core_message/message_drawer_view_conversation_header_content_type_self",DAY:"core_message/message_drawer_view_conversation_body_day",MESSAGE:"core_message/message_drawer_view_conversation_body_message",MESSAGES:"core_message/message_drawer_view_conversation_body_messages"},CONVERSATION_TYPES:{PRIVATE:1,PUBLIC:2,SELF:3},NEWEST_MESSAGES_FIRST:!0,LOAD_MESSAGE_LIMIT:100,MILLISECONDS_IN_SEC:1e3}})); +define("core_message/message_drawer_view_conversation_constants",[],(function(){return{SELECTORS:{ACTION_ACCEPT_CONTACT_REQUEST:'[data-action="accept-contact-request"]',ACTION_CANCEL_CONFIRM:'[data-action="cancel-confirm"]',ACTION_OKAY_CONFIRM:'[data-action="okay-confirm"]',ACTION_CANCEL_EDIT_MODE:'[data-action="cancel-edit-mode"]',ACTION_CONFIRM_ADD_CONTACT:'[data-action="confirm-add-contact"]',ACTION_CONFIRM_BLOCK:'[data-action="confirm-block"]',ACTION_CONFIRM_DELETE_SELECTED_MESSAGES:'[data-action="confirm-delete-selected-messages"]',ACTION_CONFIRM_DELETE_CONVERSATION:'[data-action="confirm-delete-conversation"]',ACTION_CONFIRM_FAVOURITE:'[data-action="confirm-favourite"]',ACTION_CONFIRM_MUTE:'[data-action="confirm-mute"]',ACTION_CONFIRM_UNFAVOURITE:'[data-action="confirm-unfavourite"]',ACTION_CONFIRM_REMOVE_CONTACT:'[data-action="confirm-remove-contact"]',ACTION_CONFIRM_UNBLOCK:'[data-action="confirm-unblock"]',ACTION_CONFIRM_UNMUTE:'[data-action="confirm-unmute"]',ACTION_DECLINE_CONTACT_REQUEST:'[data-action="decline-contact-request"]',ACTION_REQUEST_ADD_CONTACT:'[data-action="request-add-contact"]',ACTION_REQUEST_BLOCK:'[data-action="request-block"]',ACTION_REQUEST_DELETE_CONVERSATION:'[data-action="request-delete-conversation"]',ACTION_REQUEST_DELETE_SELECTED_MESSAGES:'[data-action="delete-selected-messages"]',ACTION_REQUEST_REMOVE_CONTACT:'[data-action="request-remove-contact"]',ACTION_REQUEST_UNBLOCK:'[data-action="request-unblock"]',ACTION_VIEW_CONTACT:'[data-action="view-contact"]',ACTION_VIEW_GROUP_INFO:'[data-action="view-group-info"]',CAN_RECEIVE_FOCUS:'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',CONFIRM_DIALOGUE:'[data-region="confirm-dialogue"]',CONFIRM_DIALOGUE_BUTTON_TEXT:'[data-region="dialogue-button-text"]',CONFIRM_DIALOGUE_CANCEL_BUTTON:'[data-action="cancel-confirm"]',CONFIRM_DIALOGUE_CONTAINER:'[data-region="confirm-dialogue-container"]',CONFIRM_DIALOGUE_HEADER:'[data-region="dialogue-header"]',CONFIRM_DIALOGUE_OKAY_BUTTON:'[data-action="okay-confirm"]',CONFIRM_DIALOGUE_TEXT:'[data-region="dialogue-text"]',CONTACT_REQUEST_SENT_MESSAGE_CONTAINER:'[data-region="contact-request-sent-message-container"]',CONTENT_PLACEHOLDER_CONTAINER:'[data-region="content-placeholder"]',CONTENT_CONTAINER:'[data-region="content-container"]',CONTENT_MESSAGES_CONTAINER:'[data-region="content-message-container"]',CONTENT_MESSAGES_FOOTER_CONTAINER:'[data-region="content-messages-footer-container"]',CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER:'[data-region="content-messages-footer-edit-mode-container"]',CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER:'[data-region="content-messages-footer-require-contact-container"]',CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER:'[data-region="content-messages-footer-require-unblock-container"]',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"]',EMOJI_AUTO_COMPLETE_CONTAINER:'[data-region="emoji-auto-complete-container"]',EMOJI_PICKER_CONTAINER:'[data-region="emoji-picker-container"]',EMOJI_PICKER:'[data-region="emoji-picker"]',EMOJI_PICKER_SEARCH_INPUT:'[data-region="search-input"]',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"]',HEADER_EDIT_MODE:'[data-region="header-edit-mode"]',HEADER_PLACEHOLDER_CONTAINER:'[data-region="header-placeholder"]',LOADING_ICON_CONTAINER:'[data-region="loading-icon-container"]',MESSAGE:'[data-region="message"]',MESSAGE_NOT_SELECTED:'[data-region="message"][aria-checked="false"]',MESSAGE_NOT_SELECTED_ICON:'[data-region="not-selected-icon"]',MESSAGE_SELECTED_ICON:'[data-region="selected-icon"]',MESSAGES:'[data-region="content-message-container"]',MESSAGES_CONTAINER:'[data-region="content-message-container"]',MESSAGES_SELECTED_COUNT:'[data-region="message-selected-court"]',MESSAGE_TEXT_AREA:'[data-region="send-message-txt"]',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"]',TOGGLE_EMOJI_PICKER_BUTTON:'[data-action="toggle-emoji-picker"]',DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER:'[data-region="day-message-unable-to-send-container"]',UNABLE_TO_MESSAGE_CONTAINER:'[data-region="unable-to-send-container"]'},TEMPLATES:{HEADER_PRIVATE:"core_message/message_drawer_view_conversation_header_content_type_private",HEADER_PRIVATE_NO_CONTROLS:"core_message/message_drawer_view_conversation_header_content_type_private_no_controls",HEADER_PUBLIC:"core_message/message_drawer_view_conversation_header_content_type_public",HEADER_SELF:"core_message/message_drawer_view_conversation_header_content_type_self",DAY:"core_message/message_drawer_view_conversation_body_day",MESSAGE:"core_message/message_drawer_view_conversation_body_message",MESSAGES:"core_message/message_drawer_view_conversation_body_messages"},CONVERSATION_TYPES:{PRIVATE:1,PUBLIC:2,SELF:3},NEWEST_MESSAGES_FIRST:!0,LOAD_MESSAGE_LIMIT:100,MILLISECONDS_IN_SEC:1e3}})); //# sourceMappingURL=message_drawer_view_conversation_constants.min.js.map \ No newline at end of file diff --git a/public/message/amd/build/message_drawer_view_conversation_constants.min.js.map b/public/message/amd/build/message_drawer_view_conversation_constants.min.js.map index eed5c512f1b..f185625e34a 100644 --- a/public/message/amd/build/message_drawer_view_conversation_constants.min.js.map +++ b/public/message/amd/build/message_drawer_view_conversation_constants.min.js.map @@ -1 +1 @@ -{"version":3,"file":"message_drawer_view_conversation_constants.min.js","sources":["../src/message_drawer_view_conversation_constants.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 .\n\n/**\n * Constant values for the conversation page in the message drawer.\n *\n * @module core_message/message_drawer_view_conversation_constants\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n\n var SELECTORS = {\n ACTION_ACCEPT_CONTACT_REQUEST: '[data-action=\"accept-contact-request\"]',\n ACTION_CANCEL_CONFIRM: '[data-action=\"cancel-confirm\"]',\n ACTION_OKAY_CONFIRM: '[data-action=\"okay-confirm\"]',\n ACTION_CANCEL_EDIT_MODE: '[data-action=\"cancel-edit-mode\"]',\n ACTION_CONFIRM_ADD_CONTACT: '[data-action=\"confirm-add-contact\"]',\n ACTION_CONFIRM_BLOCK: '[data-action=\"confirm-block\"]',\n ACTION_CONFIRM_DELETE_SELECTED_MESSAGES: '[data-action=\"confirm-delete-selected-messages\"]',\n ACTION_CONFIRM_DELETE_CONVERSATION: '[data-action=\"confirm-delete-conversation\"]',\n ACTION_CONFIRM_FAVOURITE: '[data-action=\"confirm-favourite\"]',\n ACTION_CONFIRM_MUTE: '[data-action=\"confirm-mute\"]',\n ACTION_CONFIRM_UNFAVOURITE: '[data-action=\"confirm-unfavourite\"]',\n ACTION_CONFIRM_REMOVE_CONTACT: '[data-action=\"confirm-remove-contact\"]',\n ACTION_CONFIRM_UNBLOCK: '[data-action=\"confirm-unblock\"]',\n ACTION_CONFIRM_UNMUTE: '[data-action=\"confirm-unmute\"]',\n ACTION_DECLINE_CONTACT_REQUEST: '[data-action=\"decline-contact-request\"]',\n ACTION_REQUEST_ADD_CONTACT: '[data-action=\"request-add-contact\"]',\n ACTION_REQUEST_BLOCK: '[data-action=\"request-block\"]',\n ACTION_REQUEST_DELETE_CONVERSATION: '[data-action=\"request-delete-conversation\"]',\n ACTION_REQUEST_DELETE_SELECTED_MESSAGES: '[data-action=\"delete-selected-messages\"]',\n ACTION_REQUEST_REMOVE_CONTACT: '[data-action=\"request-remove-contact\"]',\n ACTION_REQUEST_UNBLOCK: '[data-action=\"request-unblock\"]',\n ACTION_VIEW_CONTACT: '[data-action=\"view-contact\"]',\n ACTION_VIEW_GROUP_INFO: '[data-action=\"view-group-info\"]',\n CAN_RECEIVE_FOCUS: 'input:not([type=\"hidden\"]), a[href], button, textarea, select, [tabindex]',\n CONFIRM_DIALOGUE: '[data-region=\"confirm-dialogue\"]',\n CONFIRM_DIALOGUE_BUTTON_TEXT: '[data-region=\"dialogue-button-text\"]',\n CONFIRM_DIALOGUE_CANCEL_BUTTON: '[data-action=\"cancel-confirm\"]',\n CONFIRM_DIALOGUE_CONTAINER: '[data-region=\"confirm-dialogue-container\"]',\n CONFIRM_DIALOGUE_HEADER: '[data-region=\"dialogue-header\"]',\n CONFIRM_DIALOGUE_OKAY_BUTTON: '[data-action=\"okay-confirm\"]',\n CONFIRM_DIALOGUE_TEXT: '[data-region=\"dialogue-text\"]',\n CONTACT_REQUEST_SENT_MESSAGE_CONTAINER: '[data-region=\"contact-request-sent-message-container\"]',\n CONTENT_PLACEHOLDER_CONTAINER: '[data-region=\"content-placeholder\"]',\n CONTENT_CONTAINER: '[data-region=\"content-container\"]',\n CONTENT_MESSAGES_CONTAINER: '[data-region=\"content-message-container\"]',\n CONTENT_MESSAGES_FOOTER_CONTAINER: '[data-region=\"content-messages-footer-container\"]',\n CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER: '[data-region=\"content-messages-footer-edit-mode-container\"]',\n CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER: '[data-region=\"content-messages-footer-require-contact-container\"]',\n CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER: '[data-region=\"content-messages-footer-require-unblock-container\"]',\n CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER: '[data-region=\"content-messages-footer-unable-to-message\"]',\n DAY_MESSAGES_CONTAINER: '[data-region=\"day-messages-container\"]',\n DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE: '[data-region=\"delete-messages-for-all-users-toggle\"]',\n DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER: '[data-region=\"delete-messages-for-all-users-toggle-container\"]',\n EMOJI_AUTO_COMPLETE_CONTAINER: '[data-region=\"emoji-auto-complete-container\"]',\n EMOJI_PICKER_CONTAINER: '[data-region=\"emoji-picker-container\"]',\n EMOJI_PICKER: '[data-region=\"emoji-picker\"]',\n EMOJI_PICKER_SEARCH_INPUT: '[data-region=\"search-input\"]',\n ERROR_MESSAGE_CONTAINER: '[data-region=\"error-message-container\"]',\n ERROR_MESSAGE: '[data-region=\"error-message\"]',\n FAVOURITE_ICON_CONTAINER: '[data-region=\"favourite-icon-container\"]',\n FOOTER_CONTAINER: '[data-region=\"content-messages-footer-container\"]',\n HEADER: '[data-region=\"header-content\"]',\n HEADER_EDIT_MODE: '[data-region=\"header-edit-mode\"]',\n HEADER_PLACEHOLDER_CONTAINER: '[data-region=\"header-placeholder\"]',\n LOADING_ICON_CONTAINER: '[data-region=\"loading-icon-container\"]',\n MESSAGE: '[data-region=\"message\"]',\n MESSAGE_NOT_SELECTED: '[data-region=\"message\"][aria-checked=\"false\"]',\n MESSAGE_NOT_SELECTED_ICON: '[data-region=\"not-selected-icon\"]',\n MESSAGE_SELECTED_ICON: '[data-region=\"selected-icon\"]',\n MESSAGES: '[data-region=\"content-message-container\"]',\n MESSAGES_CONTAINER: '[data-region=\"content-message-container\"]',\n MESSAGES_SELECTED_COUNT: '[data-region=\"message-selected-court\"]',\n MESSAGE_TEXT_AREA: '[data-region=\"send-message-txt\"]',\n MORE_MESSAGES_LOADING_ICON_CONTAINER: '[data-region=\"more-messages-loading-icon-container\"]',\n MUTED_ICON_CONTAINER: '[data-region=\"muted-icon-container\"]',\n PLACEHOLDER_CONTAINER: '[data-region=\"placeholder-container\"]',\n RETRY_SEND: '[data-region=\"retry-send\"]',\n SELF_CONVERSATION_MESSAGE_CONTAINER: '[data-region=\"self-conversation-message-container\"]',\n SEND_MESSAGE_BUTTON: '[data-action=\"send-message\"]',\n SEND_MESSAGE_ICON_CONTAINER: '[data-region=\"send-icon-container\"]',\n TEXT: '[data-region=\"text\"]',\n TEXT_CONTAINER: '[data-region=\"text-container\"]',\n TIME_CREATED: '[data-region=\"time-created\"]',\n TITLE: '[data-region=\"title\"]',\n TOGGLE_EMOJI_PICKER_BUTTON: '[data-action=\"toggle-emoji-picker\"]'\n };\n\n var TEMPLATES = {\n HEADER_PRIVATE: 'core_message/message_drawer_view_conversation_header_content_type_private',\n HEADER_PRIVATE_NO_CONTROLS: 'core_message/message_drawer_view_conversation_header_content_type_private_no_controls',\n HEADER_PUBLIC: 'core_message/message_drawer_view_conversation_header_content_type_public',\n HEADER_SELF: 'core_message/message_drawer_view_conversation_header_content_type_self',\n DAY: 'core_message/message_drawer_view_conversation_body_day',\n MESSAGE: 'core_message/message_drawer_view_conversation_body_message',\n MESSAGES: 'core_message/message_drawer_view_conversation_body_messages'\n };\n\n // Conversation types. They must have the same values defined in \\core_message\\api.\n var CONVERSATION_TYPES = {\n PRIVATE: 1,\n PUBLIC: 2,\n SELF: 3\n };\n\n return {\n SELECTORS: SELECTORS,\n TEMPLATES: TEMPLATES,\n CONVERSATION_TYPES: CONVERSATION_TYPES,\n NEWEST_MESSAGES_FIRST: true,\n LOAD_MESSAGE_LIMIT: 100,\n MILLISECONDS_IN_SEC: 1000\n };\n});\n"],"names":["define","SELECTORS","ACTION_ACCEPT_CONTACT_REQUEST","ACTION_CANCEL_CONFIRM","ACTION_OKAY_CONFIRM","ACTION_CANCEL_EDIT_MODE","ACTION_CONFIRM_ADD_CONTACT","ACTION_CONFIRM_BLOCK","ACTION_CONFIRM_DELETE_SELECTED_MESSAGES","ACTION_CONFIRM_DELETE_CONVERSATION","ACTION_CONFIRM_FAVOURITE","ACTION_CONFIRM_MUTE","ACTION_CONFIRM_UNFAVOURITE","ACTION_CONFIRM_REMOVE_CONTACT","ACTION_CONFIRM_UNBLOCK","ACTION_CONFIRM_UNMUTE","ACTION_DECLINE_CONTACT_REQUEST","ACTION_REQUEST_ADD_CONTACT","ACTION_REQUEST_BLOCK","ACTION_REQUEST_DELETE_CONVERSATION","ACTION_REQUEST_DELETE_SELECTED_MESSAGES","ACTION_REQUEST_REMOVE_CONTACT","ACTION_REQUEST_UNBLOCK","ACTION_VIEW_CONTACT","ACTION_VIEW_GROUP_INFO","CAN_RECEIVE_FOCUS","CONFIRM_DIALOGUE","CONFIRM_DIALOGUE_BUTTON_TEXT","CONFIRM_DIALOGUE_CANCEL_BUTTON","CONFIRM_DIALOGUE_CONTAINER","CONFIRM_DIALOGUE_HEADER","CONFIRM_DIALOGUE_OKAY_BUTTON","CONFIRM_DIALOGUE_TEXT","CONTACT_REQUEST_SENT_MESSAGE_CONTAINER","CONTENT_PLACEHOLDER_CONTAINER","CONTENT_CONTAINER","CONTENT_MESSAGES_CONTAINER","CONTENT_MESSAGES_FOOTER_CONTAINER","CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER","CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER","CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER","CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER","DAY_MESSAGES_CONTAINER","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER","EMOJI_AUTO_COMPLETE_CONTAINER","EMOJI_PICKER_CONTAINER","EMOJI_PICKER","EMOJI_PICKER_SEARCH_INPUT","ERROR_MESSAGE_CONTAINER","ERROR_MESSAGE","FAVOURITE_ICON_CONTAINER","FOOTER_CONTAINER","HEADER","HEADER_EDIT_MODE","HEADER_PLACEHOLDER_CONTAINER","LOADING_ICON_CONTAINER","MESSAGE","MESSAGE_NOT_SELECTED","MESSAGE_NOT_SELECTED_ICON","MESSAGE_SELECTED_ICON","MESSAGES","MESSAGES_CONTAINER","MESSAGES_SELECTED_COUNT","MESSAGE_TEXT_AREA","MORE_MESSAGES_LOADING_ICON_CONTAINER","MUTED_ICON_CONTAINER","PLACEHOLDER_CONTAINER","RETRY_SEND","SELF_CONVERSATION_MESSAGE_CONTAINER","SEND_MESSAGE_BUTTON","SEND_MESSAGE_ICON_CONTAINER","TEXT","TEXT_CONTAINER","TIME_CREATED","TITLE","TOGGLE_EMOJI_PICKER_BUTTON","TEMPLATES","HEADER_PRIVATE","HEADER_PRIVATE_NO_CONTROLS","HEADER_PUBLIC","HEADER_SELF","DAY","CONVERSATION_TYPES","PRIVATE","PUBLIC","SELF","NEWEST_MESSAGES_FIRST","LOAD_MESSAGE_LIMIT","MILLISECONDS_IN_SEC"],"mappings":";;;;;;;AAsBAA,iEAAO,IAAI,iBAiGA,CACHC,UAhGY,CACZC,8BAA+B,yCAC/BC,sBAAuB,iCACvBC,oBAAqB,+BACrBC,wBAAyB,mCACzBC,2BAA4B,sCAC5BC,qBAAsB,gCACtBC,wCAAyC,mDACzCC,mCAAoC,8CACpCC,yBAA0B,oCAC1BC,oBAAqB,+BACrBC,2BAA4B,sCAC5BC,8BAA+B,yCAC/BC,uBAAwB,kCACxBC,sBAAuB,iCACvBC,+BAAgC,0CAChCC,2BAA4B,sCAC5BC,qBAAsB,gCACtBC,mCAAoC,8CACpCC,wCAAyC,2CACzCC,8BAA+B,yCAC/BC,uBAAwB,kCACxBC,oBAAqB,+BACrBC,uBAAwB,kCACxBC,kBAAmB,4EACnBC,iBAAkB,mCAClBC,6BAA8B,uCAC9BC,+BAAgC,iCAChCC,2BAA4B,6CAC5BC,wBAAyB,kCACzBC,6BAA8B,+BAC9BC,sBAAuB,gCACvBC,uCAAwC,yDACxCC,8BAA+B,sCAC/BC,kBAAmB,oCACnBC,2BAA4B,4CAC5BC,kCAAmC,oDACnCC,4CAA6C,8DAC7CC,kDAAmD,oEACnDC,kDAAmD,oEACnDC,oDAAqD,4DACrDC,uBAAwB,yCACxBC,qCAAsC,uDACtCC,+CAAgD,iEAChDC,8BAA+B,gDAC/BC,uBAAwB,yCACxBC,aAAc,+BACdC,0BAA2B,+BAC3BC,wBAAyB,0CACzBC,cAAe,gCACfC,yBAA0B,2CAC1BC,iBAAkB,oDAClBC,OAAQ,iCACRC,iBAAkB,mCAClBC,6BAA8B,qCAC9BC,uBAAwB,yCACxBC,QAAS,0BACTC,qBAAsB,gDACtBC,0BAA2B,oCAC3BC,sBAAuB,gCACvBC,SAAU,4CACVC,mBAAoB,4CACpBC,wBAAyB,yCACzBC,kBAAmB,mCACnBC,qCAAsC,uDACtCC,qBAAsB,uCACtBC,sBAAuB,wCACvBC,WAAY,6BACZC,oCAAqC,sDACrCC,oBAAqB,+BACrBC,4BAA6B,sCAC7BC,KAAM,uBACNC,eAAgB,iCAChBC,aAAc,+BACdC,MAAO,wBACPC,2BAA4B,uCAsB5BC,UAnBY,CACZC,eAAgB,4EAChBC,2BAA4B,wFAC5BC,cAAe,2EACfC,YAAa,yEACbC,IAAK,yDACLzB,QAAS,6DACTI,SAAU,+DAaVsB,mBATqB,CACrBC,QAAS,EACTC,OAAQ,EACRC,KAAM,GAONC,uBAAuB,EACvBC,mBAAoB,IACpBC,oBAAqB"} \ No newline at end of file +{"version":3,"file":"message_drawer_view_conversation_constants.min.js","sources":["../src/message_drawer_view_conversation_constants.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 .\n\n/**\n * Constant values for the conversation page in the message drawer.\n *\n * @module core_message/message_drawer_view_conversation_constants\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n\n var SELECTORS = {\n ACTION_ACCEPT_CONTACT_REQUEST: '[data-action=\"accept-contact-request\"]',\n ACTION_CANCEL_CONFIRM: '[data-action=\"cancel-confirm\"]',\n ACTION_OKAY_CONFIRM: '[data-action=\"okay-confirm\"]',\n ACTION_CANCEL_EDIT_MODE: '[data-action=\"cancel-edit-mode\"]',\n ACTION_CONFIRM_ADD_CONTACT: '[data-action=\"confirm-add-contact\"]',\n ACTION_CONFIRM_BLOCK: '[data-action=\"confirm-block\"]',\n ACTION_CONFIRM_DELETE_SELECTED_MESSAGES: '[data-action=\"confirm-delete-selected-messages\"]',\n ACTION_CONFIRM_DELETE_CONVERSATION: '[data-action=\"confirm-delete-conversation\"]',\n ACTION_CONFIRM_FAVOURITE: '[data-action=\"confirm-favourite\"]',\n ACTION_CONFIRM_MUTE: '[data-action=\"confirm-mute\"]',\n ACTION_CONFIRM_UNFAVOURITE: '[data-action=\"confirm-unfavourite\"]',\n ACTION_CONFIRM_REMOVE_CONTACT: '[data-action=\"confirm-remove-contact\"]',\n ACTION_CONFIRM_UNBLOCK: '[data-action=\"confirm-unblock\"]',\n ACTION_CONFIRM_UNMUTE: '[data-action=\"confirm-unmute\"]',\n ACTION_DECLINE_CONTACT_REQUEST: '[data-action=\"decline-contact-request\"]',\n ACTION_REQUEST_ADD_CONTACT: '[data-action=\"request-add-contact\"]',\n ACTION_REQUEST_BLOCK: '[data-action=\"request-block\"]',\n ACTION_REQUEST_DELETE_CONVERSATION: '[data-action=\"request-delete-conversation\"]',\n ACTION_REQUEST_DELETE_SELECTED_MESSAGES: '[data-action=\"delete-selected-messages\"]',\n ACTION_REQUEST_REMOVE_CONTACT: '[data-action=\"request-remove-contact\"]',\n ACTION_REQUEST_UNBLOCK: '[data-action=\"request-unblock\"]',\n ACTION_VIEW_CONTACT: '[data-action=\"view-contact\"]',\n ACTION_VIEW_GROUP_INFO: '[data-action=\"view-group-info\"]',\n CAN_RECEIVE_FOCUS: 'input:not([type=\"hidden\"]), a[href], button, textarea, select, [tabindex]',\n CONFIRM_DIALOGUE: '[data-region=\"confirm-dialogue\"]',\n CONFIRM_DIALOGUE_BUTTON_TEXT: '[data-region=\"dialogue-button-text\"]',\n CONFIRM_DIALOGUE_CANCEL_BUTTON: '[data-action=\"cancel-confirm\"]',\n CONFIRM_DIALOGUE_CONTAINER: '[data-region=\"confirm-dialogue-container\"]',\n CONFIRM_DIALOGUE_HEADER: '[data-region=\"dialogue-header\"]',\n CONFIRM_DIALOGUE_OKAY_BUTTON: '[data-action=\"okay-confirm\"]',\n CONFIRM_DIALOGUE_TEXT: '[data-region=\"dialogue-text\"]',\n CONTACT_REQUEST_SENT_MESSAGE_CONTAINER: '[data-region=\"contact-request-sent-message-container\"]',\n CONTENT_PLACEHOLDER_CONTAINER: '[data-region=\"content-placeholder\"]',\n CONTENT_CONTAINER: '[data-region=\"content-container\"]',\n CONTENT_MESSAGES_CONTAINER: '[data-region=\"content-message-container\"]',\n CONTENT_MESSAGES_FOOTER_CONTAINER: '[data-region=\"content-messages-footer-container\"]',\n CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER: '[data-region=\"content-messages-footer-edit-mode-container\"]',\n CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER: '[data-region=\"content-messages-footer-require-contact-container\"]',\n CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER: '[data-region=\"content-messages-footer-require-unblock-container\"]',\n DAY_MESSAGES_CONTAINER: '[data-region=\"day-messages-container\"]',\n DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE: '[data-region=\"delete-messages-for-all-users-toggle\"]',\n DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER: '[data-region=\"delete-messages-for-all-users-toggle-container\"]',\n EMOJI_AUTO_COMPLETE_CONTAINER: '[data-region=\"emoji-auto-complete-container\"]',\n EMOJI_PICKER_CONTAINER: '[data-region=\"emoji-picker-container\"]',\n EMOJI_PICKER: '[data-region=\"emoji-picker\"]',\n EMOJI_PICKER_SEARCH_INPUT: '[data-region=\"search-input\"]',\n ERROR_MESSAGE_CONTAINER: '[data-region=\"error-message-container\"]',\n ERROR_MESSAGE: '[data-region=\"error-message\"]',\n FAVOURITE_ICON_CONTAINER: '[data-region=\"favourite-icon-container\"]',\n FOOTER_CONTAINER: '[data-region=\"content-messages-footer-container\"]',\n HEADER: '[data-region=\"header-content\"]',\n HEADER_EDIT_MODE: '[data-region=\"header-edit-mode\"]',\n HEADER_PLACEHOLDER_CONTAINER: '[data-region=\"header-placeholder\"]',\n LOADING_ICON_CONTAINER: '[data-region=\"loading-icon-container\"]',\n MESSAGE: '[data-region=\"message\"]',\n MESSAGE_NOT_SELECTED: '[data-region=\"message\"][aria-checked=\"false\"]',\n MESSAGE_NOT_SELECTED_ICON: '[data-region=\"not-selected-icon\"]',\n MESSAGE_SELECTED_ICON: '[data-region=\"selected-icon\"]',\n MESSAGES: '[data-region=\"content-message-container\"]',\n MESSAGES_CONTAINER: '[data-region=\"content-message-container\"]',\n MESSAGES_SELECTED_COUNT: '[data-region=\"message-selected-court\"]',\n MESSAGE_TEXT_AREA: '[data-region=\"send-message-txt\"]',\n MORE_MESSAGES_LOADING_ICON_CONTAINER: '[data-region=\"more-messages-loading-icon-container\"]',\n MUTED_ICON_CONTAINER: '[data-region=\"muted-icon-container\"]',\n PLACEHOLDER_CONTAINER: '[data-region=\"placeholder-container\"]',\n RETRY_SEND: '[data-region=\"retry-send\"]',\n SELF_CONVERSATION_MESSAGE_CONTAINER: '[data-region=\"self-conversation-message-container\"]',\n SEND_MESSAGE_BUTTON: '[data-action=\"send-message\"]',\n SEND_MESSAGE_ICON_CONTAINER: '[data-region=\"send-icon-container\"]',\n TEXT: '[data-region=\"text\"]',\n TEXT_CONTAINER: '[data-region=\"text-container\"]',\n TIME_CREATED: '[data-region=\"time-created\"]',\n TITLE: '[data-region=\"title\"]',\n TOGGLE_EMOJI_PICKER_BUTTON: '[data-action=\"toggle-emoji-picker\"]',\n DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER: '[data-region=\"day-message-unable-to-send-container\"]',\n UNABLE_TO_MESSAGE_CONTAINER: '[data-region=\"unable-to-send-container\"]',\n };\n\n var TEMPLATES = {\n HEADER_PRIVATE: 'core_message/message_drawer_view_conversation_header_content_type_private',\n HEADER_PRIVATE_NO_CONTROLS: 'core_message/message_drawer_view_conversation_header_content_type_private_no_controls',\n HEADER_PUBLIC: 'core_message/message_drawer_view_conversation_header_content_type_public',\n HEADER_SELF: 'core_message/message_drawer_view_conversation_header_content_type_self',\n DAY: 'core_message/message_drawer_view_conversation_body_day',\n MESSAGE: 'core_message/message_drawer_view_conversation_body_message',\n MESSAGES: 'core_message/message_drawer_view_conversation_body_messages'\n };\n\n // Conversation types. They must have the same values defined in \\core_message\\api.\n var CONVERSATION_TYPES = {\n PRIVATE: 1,\n PUBLIC: 2,\n SELF: 3\n };\n\n return {\n SELECTORS: SELECTORS,\n TEMPLATES: TEMPLATES,\n CONVERSATION_TYPES: CONVERSATION_TYPES,\n NEWEST_MESSAGES_FIRST: true,\n LOAD_MESSAGE_LIMIT: 100,\n MILLISECONDS_IN_SEC: 1000\n };\n});\n"],"names":["define","SELECTORS","ACTION_ACCEPT_CONTACT_REQUEST","ACTION_CANCEL_CONFIRM","ACTION_OKAY_CONFIRM","ACTION_CANCEL_EDIT_MODE","ACTION_CONFIRM_ADD_CONTACT","ACTION_CONFIRM_BLOCK","ACTION_CONFIRM_DELETE_SELECTED_MESSAGES","ACTION_CONFIRM_DELETE_CONVERSATION","ACTION_CONFIRM_FAVOURITE","ACTION_CONFIRM_MUTE","ACTION_CONFIRM_UNFAVOURITE","ACTION_CONFIRM_REMOVE_CONTACT","ACTION_CONFIRM_UNBLOCK","ACTION_CONFIRM_UNMUTE","ACTION_DECLINE_CONTACT_REQUEST","ACTION_REQUEST_ADD_CONTACT","ACTION_REQUEST_BLOCK","ACTION_REQUEST_DELETE_CONVERSATION","ACTION_REQUEST_DELETE_SELECTED_MESSAGES","ACTION_REQUEST_REMOVE_CONTACT","ACTION_REQUEST_UNBLOCK","ACTION_VIEW_CONTACT","ACTION_VIEW_GROUP_INFO","CAN_RECEIVE_FOCUS","CONFIRM_DIALOGUE","CONFIRM_DIALOGUE_BUTTON_TEXT","CONFIRM_DIALOGUE_CANCEL_BUTTON","CONFIRM_DIALOGUE_CONTAINER","CONFIRM_DIALOGUE_HEADER","CONFIRM_DIALOGUE_OKAY_BUTTON","CONFIRM_DIALOGUE_TEXT","CONTACT_REQUEST_SENT_MESSAGE_CONTAINER","CONTENT_PLACEHOLDER_CONTAINER","CONTENT_CONTAINER","CONTENT_MESSAGES_CONTAINER","CONTENT_MESSAGES_FOOTER_CONTAINER","CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER","CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER","CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER","DAY_MESSAGES_CONTAINER","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER","EMOJI_AUTO_COMPLETE_CONTAINER","EMOJI_PICKER_CONTAINER","EMOJI_PICKER","EMOJI_PICKER_SEARCH_INPUT","ERROR_MESSAGE_CONTAINER","ERROR_MESSAGE","FAVOURITE_ICON_CONTAINER","FOOTER_CONTAINER","HEADER","HEADER_EDIT_MODE","HEADER_PLACEHOLDER_CONTAINER","LOADING_ICON_CONTAINER","MESSAGE","MESSAGE_NOT_SELECTED","MESSAGE_NOT_SELECTED_ICON","MESSAGE_SELECTED_ICON","MESSAGES","MESSAGES_CONTAINER","MESSAGES_SELECTED_COUNT","MESSAGE_TEXT_AREA","MORE_MESSAGES_LOADING_ICON_CONTAINER","MUTED_ICON_CONTAINER","PLACEHOLDER_CONTAINER","RETRY_SEND","SELF_CONVERSATION_MESSAGE_CONTAINER","SEND_MESSAGE_BUTTON","SEND_MESSAGE_ICON_CONTAINER","TEXT","TEXT_CONTAINER","TIME_CREATED","TITLE","TOGGLE_EMOJI_PICKER_BUTTON","DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER","UNABLE_TO_MESSAGE_CONTAINER","TEMPLATES","HEADER_PRIVATE","HEADER_PRIVATE_NO_CONTROLS","HEADER_PUBLIC","HEADER_SELF","DAY","CONVERSATION_TYPES","PRIVATE","PUBLIC","SELF","NEWEST_MESSAGES_FIRST","LOAD_MESSAGE_LIMIT","MILLISECONDS_IN_SEC"],"mappings":";;;;;;;AAsBAA,iEAAO,IAAI,iBAkGA,CACHC,UAjGY,CACZC,8BAA+B,yCAC/BC,sBAAuB,iCACvBC,oBAAqB,+BACrBC,wBAAyB,mCACzBC,2BAA4B,sCAC5BC,qBAAsB,gCACtBC,wCAAyC,mDACzCC,mCAAoC,8CACpCC,yBAA0B,oCAC1BC,oBAAqB,+BACrBC,2BAA4B,sCAC5BC,8BAA+B,yCAC/BC,uBAAwB,kCACxBC,sBAAuB,iCACvBC,+BAAgC,0CAChCC,2BAA4B,sCAC5BC,qBAAsB,gCACtBC,mCAAoC,8CACpCC,wCAAyC,2CACzCC,8BAA+B,yCAC/BC,uBAAwB,kCACxBC,oBAAqB,+BACrBC,uBAAwB,kCACxBC,kBAAmB,4EACnBC,iBAAkB,mCAClBC,6BAA8B,uCAC9BC,+BAAgC,iCAChCC,2BAA4B,6CAC5BC,wBAAyB,kCACzBC,6BAA8B,+BAC9BC,sBAAuB,gCACvBC,uCAAwC,yDACxCC,8BAA+B,sCAC/BC,kBAAmB,oCACnBC,2BAA4B,4CAC5BC,kCAAmC,oDACnCC,4CAA6C,8DAC7CC,kDAAmD,oEACnDC,kDAAmD,oEACnDC,uBAAwB,yCACxBC,qCAAsC,uDACtCC,+CAAgD,iEAChDC,8BAA+B,gDAC/BC,uBAAwB,yCACxBC,aAAc,+BACdC,0BAA2B,+BAC3BC,wBAAyB,0CACzBC,cAAe,gCACfC,yBAA0B,2CAC1BC,iBAAkB,oDAClBC,OAAQ,iCACRC,iBAAkB,mCAClBC,6BAA8B,qCAC9BC,uBAAwB,yCACxBC,QAAS,0BACTC,qBAAsB,gDACtBC,0BAA2B,oCAC3BC,sBAAuB,gCACvBC,SAAU,4CACVC,mBAAoB,4CACpBC,wBAAyB,yCACzBC,kBAAmB,mCACnBC,qCAAsC,uDACtCC,qBAAsB,uCACtBC,sBAAuB,wCACvBC,WAAY,6BACZC,oCAAqC,sDACrCC,oBAAqB,+BACrBC,4BAA6B,sCAC7BC,KAAM,uBACNC,eAAgB,iCAChBC,aAAc,+BACdC,MAAO,wBACPC,2BAA4B,sCAC5BC,qCAAsC,uDACtCC,4BAA6B,4CAsB7BC,UAnBY,CACZC,eAAgB,4EAChBC,2BAA4B,wFAC5BC,cAAe,2EACfC,YAAa,yEACbC,IAAK,yDACL3B,QAAS,6DACTI,SAAU,+DAaVwB,mBATqB,CACrBC,QAAS,EACTC,OAAQ,EACRC,KAAM,GAONC,uBAAuB,EACvBC,mBAAoB,IACpBC,oBAAqB"} \ No newline at end of file diff --git a/public/message/amd/build/message_drawer_view_conversation_patcher.min.js b/public/message/amd/build/message_drawer_view_conversation_patcher.min.js index 6343e591114..c8b5087af1c 100644 --- a/public/message/amd/build/message_drawer_view_conversation_patcher.min.js +++ b/public/message/amd/build/message_drawer_view_conversation_patcher.min.js @@ -10,6 +10,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("core_message/message_drawer_view_conversation_patcher",["jquery","core/user_date","core_message/message_drawer_view_conversation_constants"],(function($,UserDate,Constants){var sortMessagesByDay=function(messages,midnight){var messagesByDay=messages.reduce((function(carry,message){var timeCreated=message.timeCreated?message.timeCreated:midnight,dayTimestamp=UserDate.getUserMidnightForTimestamp(timeCreated,midnight);return carry.hasOwnProperty(dayTimestamp)?carry[dayTimestamp].push(message):carry[dayTimestamp]=[message],carry}),{});return Object.keys(messagesByDay).map((function(dayTimestamp){return{timestamp:dayTimestamp,messages:messagesByDay[dayTimestamp]}}))},diffArrays=function(a,b,matchFunction){b=b.slice();var missingFromB=[],matches=[];return a.forEach((function(current){for(var found=!1,index=0;index0,newHasContactRequests=newContactRequests.length>0;return oldOtherUser||newOtherUser?oldHasContactRequests&&newHasContactRequests?null:oldHasContactRequests||!newHasContactRequests||newOtherUser.iscontact?!oldOtherUser&&newOtherUser?newOtherUser.iscontact?"contact":null:!newOtherUser&&oldOtherUser?oldOtherUser.iscontact?"non-contact":null:oldOtherUser.iscontact&&!newOtherUser.iscontact?newHasContactRequests?"pending-contact":"non-contact":!oldOtherUser.iscontact&&newOtherUser.iscontact?"contact":null:"pending-contact":null},buildLoadingConfirmationAction=function(state,newState){return!(state.loadingConfirmAction||!newState.loadingConfirmAction)||!(state.loadingConfirmAction&&!newState.loadingConfirmAction)&&null},buildInEditMode=function(state,newState){var oldHasSelectedMessages=state.selectedMessageIds.length>0,newHasSelectedMessages=newState.selectedMessageIds.length>0,numberOfMessagesHasChanged=state.messages.length!=newState.messages.length;return!(oldHasSelectedMessages||!newHasSelectedMessages)||!(oldHasSelectedMessages&&!newHasSelectedMessages)&&(!(!oldHasSelectedMessages||!numberOfMessagesHasChanged)||null)},buildSelectedMessages=function(state,newState){var oldSelectedMessages=state.selectedMessageIds,newSelectedMessages=newState.selectedMessageIds;if(isArrayEqual(oldSelectedMessages,newSelectedMessages))return null;var diff=diffArrays(oldSelectedMessages,newSelectedMessages,(function(a,b){return a==b}));return{count:newSelectedMessages.length,add:diff.missingFromA,remove:diff.missingFromB}},getOtherUserFromState=function(state){return Object.keys(state.members).reduce((function(carry,userId){return userId==state.loggedInUserId||carry||(carry=state.members[userId]),carry}),null)},requiresContactRequest=function(loggedInUserId,user){if(user.canmessage)return!1;var hasSentContactRequest=user.contactrequests.filter((function(request){return request.userid==loggedInUserId||request.requesteduserid})).length>0;return user.requirescontact&&!user.iscontact&&!hasSentContactRequest},buildRequireAddContact=function(state,newState){var oldOtherUser=getOtherUserFromState(state),newOtherUser=getOtherUserFromState(newState),hadMessages=state.messages.length>0,hasMessages=newState.messages.length>0,loggedInUserId=newState.loggedInUserId,prevRequiresContactRequest=oldOtherUser&&requiresContactRequest(loggedInUserId,oldOtherUser),nextRequiresContactRequest=newOtherUser&&requiresContactRequest(loggedInUserId,newOtherUser),finishedAddContact=!1===buildConfirmAddContact(state,newState);if(!state.hasTriedToLoadMessages&&!newState.hasTriedToLoadMessages)return null;if(!oldOtherUser&&!newOtherUser)return null;if(!oldOtherUser&&nextRequiresContactRequest)return{show:!0,hasMessages:hasMessages,user:newOtherUser};if(finishedAddContact&&nextRequiresContactRequest)return{show:!0,hasMessages:hasMessages,user:newOtherUser};if(state.hasTriedToLoadMessages&&newState.hasTriedToLoadMessages){if(!prevRequiresContactRequest&&nextRequiresContactRequest)return{show:!0,hasMessages:hasMessages,user:newOtherUser};if(prevRequiresContactRequest&&!nextRequiresContactRequest)return{show:!1,hasMessages:hasMessages}}return!state.hasTriedToLoadMessages&&newState.hasTriedToLoadMessages&&nextRequiresContactRequest?{show:!0,hasMessages:hasMessages,user:newOtherUser}:state.hasTriedToLoadMessages&&!newState.hasTriedToLoadMessages&&prevRequiresContactRequest?{show:!1,hasMessages:hadMessages}:null},buildFooterPatchTypePrivate=function(state,newState){var loadingFirstMessages=buildLoadingFirstMessages(state,newState),inEditMode=buildInEditMode(state,newState),requireAddContact=buildRequireAddContact(state,newState),requireUnblock=function(state,newState){var oldOtherUser=getOtherUserFromState(state),newOtherUser=getOtherUserFromState(newState);return oldOtherUser||newOtherUser?oldOtherUser&&!newOtherUser?!oldOtherUser.isblocked&&null:!oldOtherUser&&newOtherUser?!!newOtherUser.isblocked||null:!(oldOtherUser.isblocked||!newOtherUser.isblocked)||!(oldOtherUser.isblocked&&!newOtherUser.isblocked)&&null:null}(state,newState),unableToMessage=function(state,newState){var oldOtherUser=getOtherUserFromState(state),newOtherUser=getOtherUserFromState(newState);return newState.type==Constants.CONVERSATION_TYPES.SELF?null:oldOtherUser||newOtherUser?oldOtherUser&&!newOtherUser?!oldOtherUser.canmessage||null:!oldOtherUser&&newOtherUser?!newOtherUser.canmessage||null:!(!oldOtherUser.canmessage&&newOtherUser.canmessage)&&(!(!oldOtherUser.canmessage||newOtherUser.canmessage)||null):null}(state,newState),showRequireAddContact=null!==requireAddContact?requireAddContact.show&&requireAddContact.hasMessages:null,otherUser=getOtherUserFromState(newState),generateReturnValue=function(checkValue,successReturn){if(checkValue)return successReturn;if(null!==checkValue&&!checkValue){if(!otherUser)return{type:"content"};if(otherUser.isblocked)return{type:"unblock"};if(newState.messages.length&&requiresContactRequest(newState.loggedInUserId,otherUser))return{type:"add-contact",user:otherUser};if(!otherUser.canmessage&&otherUser.requirescontact&&!otherUser.iscontact)return{type:"unable-to-message"}}return null};if(null===loadingFirstMessages&&null===inEditMode&&null===requireAddContact&&null===requireUnblock)return null;for(var checks=[[loadingFirstMessages,{type:"placeholder"}],[inEditMode,{type:"edit-mode"}],[unableToMessage,{type:"unable-to-message"}],[requireUnblock,{type:"unblock"}],[showRequireAddContact,{type:"add-contact",user:otherUser}]],i=0;i0,newRequest=newSentRequests.length>0;return oldRequest||!newRequest||newOtherUser.iscontact?!(oldOtherUser&&!oldOtherUser.iscontact&&newRequest&&newOtherUser.iscontact)&&(!(oldRequest&&!newRequest)&&null):newOtherUser.fullname};return{buildPatch:function(state,newState){var config={all:{reset:buildReset,conversation:buildConversationPatch,scrollToMessage:buildScrollToMessagePatch,loadingMembers:buildLoadingMembersPatch,loadingFirstMessages:buildLoadingFirstMessages,loadingMessages:buildLoadingMessages,confirmDeleteSelectedMessages:buildConfirmDeleteSelectedMessages,inEditMode:buildInEditMode,selectedMessages:buildSelectedMessages,isFavourite:buildIsFavourite,isMuted:buildIsMuted,showEmojiPicker:buildShowEmojiPicker,showEmojiAutoComplete:buildShowEmojiAutoComplete}};config[Constants.CONVERSATION_TYPES.PRIVATE]={header:buildHeaderPatchTypePrivate,footer:buildFooterPatchTypePrivate,confirmBlockUser:buildConfirmBlockUser,confirmUnblockUser:buildConfirmUnblockUser,confirmAddContact:buildConfirmAddContact,confirmRemoveContact:buildConfirmRemoveContact,confirmContactRequest:buildConfirmContactRequest,confirmDeleteConversation:buildConfirmDeleteConversation,isBlocked:buildIsBlocked,isContact:buildIsContact,loadingConfirmAction:buildLoadingConfirmationAction,requireAddContact:buildRequireAddContact,contactRequestSent:buildContactRequestSent},config[Constants.CONVERSATION_TYPES.PUBLIC]={header:buildHeaderPatchTypePublic,footer:buildFooterPatchTypePublic},config[Constants.CONVERSATION_TYPES.SELF]={header:buildHeaderPatchTypeSelf,footer:buildFooterPatchTypePublic,confirmDeleteConversation:buildConfirmDeleteConversation,selfConversationMessage:buildSelfConversationMessage};var patchConfig=$.extend({},config.all);return newState.type&&newState.type in config&&(patchConfig=$.extend(patchConfig,config[newState.type])),Object.keys(patchConfig).reduce((function(patch,key){var value=(0,patchConfig[key])(state,newState);return null!==value&&(patch[key]=value),patch}),{})}}})); +define("core_message/message_drawer_view_conversation_patcher",["jquery","core/user_date","core_message/message_drawer_view_conversation_constants"],(function($,UserDate,Constants){var sortMessagesByDay=function(messages,midnight){var messagesByDay=messages.reduce((function(carry,message){var timeCreated=message.timeCreated?message.timeCreated:midnight,dayTimestamp=UserDate.getUserMidnightForTimestamp(timeCreated,midnight);return carry.hasOwnProperty(dayTimestamp)?carry[dayTimestamp].push(message):carry[dayTimestamp]=[message],carry}),{});return Object.keys(messagesByDay).map((function(dayTimestamp){return{timestamp:dayTimestamp,messages:messagesByDay[dayTimestamp]}}))},diffArrays=function(a,b,matchFunction){b=b.slice();var missingFromB=[],matches=[];return a.forEach((function(current){for(var found=!1,index=0;index0,newHasContactRequests=newContactRequests.length>0;return oldOtherUser||newOtherUser?oldHasContactRequests&&newHasContactRequests?null:oldHasContactRequests||!newHasContactRequests||newOtherUser.iscontact?!oldOtherUser&&newOtherUser?newOtherUser.iscontact?"contact":null:!newOtherUser&&oldOtherUser?oldOtherUser.iscontact?"non-contact":null:oldOtherUser.iscontact&&!newOtherUser.iscontact?newHasContactRequests?"pending-contact":"non-contact":!oldOtherUser.iscontact&&newOtherUser.iscontact?"contact":null:"pending-contact":null},buildLoadingConfirmationAction=function(state,newState){return!(state.loadingConfirmAction||!newState.loadingConfirmAction)||!(state.loadingConfirmAction&&!newState.loadingConfirmAction)&&null},buildInEditMode=function(state,newState){var oldHasSelectedMessages=state.selectedMessageIds.length>0,newHasSelectedMessages=newState.selectedMessageIds.length>0,numberOfMessagesHasChanged=state.messages.length!=newState.messages.length;return!(oldHasSelectedMessages||!newHasSelectedMessages)||!(oldHasSelectedMessages&&!newHasSelectedMessages)&&(!(!oldHasSelectedMessages||!numberOfMessagesHasChanged)||null)},buildSelectedMessages=function(state,newState){var oldSelectedMessages=state.selectedMessageIds,newSelectedMessages=newState.selectedMessageIds;if(isArrayEqual(oldSelectedMessages,newSelectedMessages))return null;var diff=diffArrays(oldSelectedMessages,newSelectedMessages,(function(a,b){return a==b}));return{count:newSelectedMessages.length,add:diff.missingFromA,remove:diff.missingFromB}},getOtherUserFromState=function(state){return Object.keys(state.members).reduce((function(carry,userId){return userId==state.loggedInUserId||carry||(carry=state.members[userId]),carry}),null)},requiresContactRequest=function(loggedInUserId,user){if(user.canmessage)return!1;var hasSentContactRequest=user.contactrequests.filter((function(request){return request.userid==loggedInUserId||request.requesteduserid})).length>0;return user.requirescontact&&!user.iscontact&&!hasSentContactRequest},buildRequireAddContact=function(state,newState){var oldOtherUser=getOtherUserFromState(state),newOtherUser=getOtherUserFromState(newState),hadMessages=state.messages.length>0,hasMessages=newState.messages.length>0,loggedInUserId=newState.loggedInUserId,prevRequiresContactRequest=oldOtherUser&&requiresContactRequest(loggedInUserId,oldOtherUser),nextRequiresContactRequest=newOtherUser&&requiresContactRequest(loggedInUserId,newOtherUser),finishedAddContact=!1===buildConfirmAddContact(state,newState);if(!state.hasTriedToLoadMessages&&!newState.hasTriedToLoadMessages)return null;if(!oldOtherUser&&!newOtherUser)return null;if(!oldOtherUser&&nextRequiresContactRequest)return{show:!0,hasMessages:hasMessages,user:newOtherUser};if(finishedAddContact&&nextRequiresContactRequest)return{show:!0,hasMessages:hasMessages,user:newOtherUser};if(state.hasTriedToLoadMessages&&newState.hasTriedToLoadMessages){if(!prevRequiresContactRequest&&nextRequiresContactRequest)return{show:!0,hasMessages:hasMessages,user:newOtherUser};if(prevRequiresContactRequest&&!nextRequiresContactRequest)return{show:!1,hasMessages:hasMessages}}return!state.hasTriedToLoadMessages&&newState.hasTriedToLoadMessages&&nextRequiresContactRequest?{show:!0,hasMessages:hasMessages,user:newOtherUser}:state.hasTriedToLoadMessages&&!newState.hasTriedToLoadMessages&&prevRequiresContactRequest?{show:!1,hasMessages:hadMessages}:null};const buildUnableToMessagePatch=(state,newState)=>{if(newState.type==Constants.CONVERSATION_TYPES.SELF)return!1;const oldOtherUser=getOtherUserFromState(state),newOtherUser=getOtherUserFromState(newState);return!(!oldOtherUser&&!newOtherUser)&&(oldOtherUser&&!newOtherUser?!oldOtherUser.canmessage:!oldOtherUser&&newOtherUser?!newOtherUser.canmessage:!(!oldOtherUser.canmessage&&newOtherUser.canmessage)&&(!(!oldOtherUser.canmessage||newOtherUser.canmessage)||!newState.canSendMessageToConversation))};var buildFooterPatchTypePrivate=function(state,newState){var loadingFirstMessages=buildLoadingFirstMessages(state,newState),inEditMode=buildInEditMode(state,newState),requireAddContact=buildRequireAddContact(state,newState),requireUnblock=function(state,newState){var oldOtherUser=getOtherUserFromState(state),newOtherUser=getOtherUserFromState(newState);return oldOtherUser||newOtherUser?oldOtherUser&&!newOtherUser?!oldOtherUser.isblocked&&null:!oldOtherUser&&newOtherUser?!!newOtherUser.isblocked||null:!(oldOtherUser.isblocked||!newOtherUser.isblocked)||!(oldOtherUser.isblocked&&!newOtherUser.isblocked)&&null:null}(state,newState),showRequireAddContact=null!==requireAddContact?requireAddContact.show&&requireAddContact.hasMessages:null,otherUser=getOtherUserFromState(newState),generateReturnValue=function(checkValue,successReturn){if(checkValue)return successReturn;if(null!==checkValue&&!checkValue){if(!otherUser)return{type:"content"};if(otherUser.isblocked)return{type:"unblock"};if(newState.messages.length&&requiresContactRequest(newState.loggedInUserId,otherUser))return{type:"add-contact",user:otherUser}}return null};if(null===loadingFirstMessages&&null===inEditMode&&null===requireAddContact&&null===requireUnblock)return null;for(var checks=[[loadingFirstMessages,{type:"placeholder"}],[inEditMode,{type:"edit-mode"}],[requireUnblock,{type:"unblock"}],[showRequireAddContact,{type:"add-contact",user:otherUser}]],i=0;i0,newRequest=newSentRequests.length>0;return oldRequest||!newRequest||newOtherUser.iscontact?!(oldOtherUser&&!oldOtherUser.iscontact&&newRequest&&newOtherUser.iscontact)&&(!(oldRequest&&!newRequest)&&null):newOtherUser.fullname};return{buildPatch:function(state,newState){var config={all:{reset:buildReset,conversation:buildConversationPatch,scrollToMessage:buildScrollToMessagePatch,loadingMembers:buildLoadingMembersPatch,loadingFirstMessages:buildLoadingFirstMessages,loadingMessages:buildLoadingMessages,confirmDeleteSelectedMessages:buildConfirmDeleteSelectedMessages,inEditMode:buildInEditMode,selectedMessages:buildSelectedMessages,isFavourite:buildIsFavourite,isMuted:buildIsMuted,showEmojiPicker:buildShowEmojiPicker,showEmojiAutoComplete:buildShowEmojiAutoComplete,unableToMessage:buildUnableToMessagePatch}};config[Constants.CONVERSATION_TYPES.PRIVATE]={header:buildHeaderPatchTypePrivate,footer:buildFooterPatchTypePrivate,confirmBlockUser:buildConfirmBlockUser,confirmUnblockUser:buildConfirmUnblockUser,confirmAddContact:buildConfirmAddContact,confirmRemoveContact:buildConfirmRemoveContact,confirmContactRequest:buildConfirmContactRequest,confirmDeleteConversation:buildConfirmDeleteConversation,isBlocked:buildIsBlocked,isContact:buildIsContact,loadingConfirmAction:buildLoadingConfirmationAction,requireAddContact:buildRequireAddContact,contactRequestSent:buildContactRequestSent},config[Constants.CONVERSATION_TYPES.PUBLIC]={header:buildHeaderPatchTypePublic,footer:buildFooterPatchTypePublic},config[Constants.CONVERSATION_TYPES.SELF]={header:buildHeaderPatchTypeSelf,footer:buildFooterPatchTypePublic,confirmDeleteConversation:buildConfirmDeleteConversation,selfConversationMessage:buildSelfConversationMessage};var patchConfig=$.extend({},config.all);return newState.type&&newState.type in config&&(patchConfig=$.extend(patchConfig,config[newState.type])),Object.keys(patchConfig).reduce((function(patch,key){var value=(0,patchConfig[key])(state,newState);return null!==value&&(patch[key]=value),patch}),{})}}})); //# sourceMappingURL=message_drawer_view_conversation_patcher.min.js.map \ No newline at end of file diff --git a/public/message/amd/build/message_drawer_view_conversation_patcher.min.js.map b/public/message/amd/build/message_drawer_view_conversation_patcher.min.js.map index 5654746af0a..9a4b52ee75d 100644 --- a/public/message/amd/build/message_drawer_view_conversation_patcher.min.js.map +++ b/public/message/amd/build/message_drawer_view_conversation_patcher.min.js.map @@ -1 +1 @@ -{"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 .\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_conversation_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 \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(timeCreated, 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 state 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.missingFromA.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 || diff.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 whole 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: 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 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 cancreatecontact: newOtherUser.cancreatecontact,\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 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 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 showfavourite: 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|Null}\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 request 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 loggedInUserId = 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 } else 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 = function(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 (oldHasSelectedMessages && 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 = newOtherUser && 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 (finishedAddContact && 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 oldOtherUser.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.requirescontact && !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 conversation 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 newSentRequests = !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 information 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 only 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","Constants","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","requiresAddContact","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","cancreatecontact","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","oldPendingCount","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","buildRequireUnblock","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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"} \ No newline at end of file +{"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 .\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_conversation_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 \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(timeCreated, 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 state 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.missingFromA.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 || diff.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 whole day.\n messages: buildMessagesPatch(daysDiff.matches),\n unableToMessage: buildUnableToMessagePatch(state, newState),\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: 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 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 cancreatecontact: newOtherUser.cancreatecontact,\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 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 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 showfavourite: 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|Null}\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 request 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 loggedInUserId = 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 } else 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 = function(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 (oldHasSelectedMessages && 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 = newOtherUser && 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 (finishedAddContact && 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 oldOtherUser.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}\n */\n const buildUnableToMessagePatch = (state, newState) => {\n if (newState.type == Constants.CONVERSATION_TYPES.SELF) {\n // Users always can send message themselves on self-conversations.\n return false;\n }\n\n const oldOtherUser = getOtherUserFromState(state);\n const newOtherUser = getOtherUserFromState(newState);\n\n if (!oldOtherUser && !newOtherUser) {\n return false;\n } else if (oldOtherUser && !newOtherUser) {\n return !oldOtherUser.canmessage;\n } else if (!oldOtherUser && newOtherUser) {\n return !newOtherUser.canmessage;\n } else if (!oldOtherUser.canmessage && newOtherUser.canmessage) {\n return false;\n } else if (oldOtherUser.canmessage && !newOtherUser.canmessage) {\n return true;\n }\n\n return !newState.canSendMessageToConversation;\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 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 }\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 [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 conversation 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 newSentRequests = !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 information 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 unableToMessage: buildUnableToMessagePatch\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 only 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","Constants","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","unableToMessage","buildUnableToMessagePatch","buildHeaderPatchTypePrivate","requireAddContact","buildRequireAddContact","confirmContactRequest","buildConfirmContactRequest","oldOtherUser","getOtherUserFromState","newOtherUser","requiresAddContact","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","cancreatecontact","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","oldPendingCount","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","canSendMessageToConversation","buildFooterPatchTypePrivate","loadingFirstMessages","inEditMode","requireUnblock","buildRequireUnblock","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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"} \ No newline at end of file diff --git a/public/message/amd/build/message_drawer_view_conversation_renderer.min.js b/public/message/amd/build/message_drawer_view_conversation_renderer.min.js index b228eaa0db9..531c321af56 100644 --- a/public/message/amd/build/message_drawer_view_conversation_renderer.min.js +++ b/public/message/amd/build/message_drawer_view_conversation_renderer.min.js @@ -11,6 +11,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("core_message/message_drawer_view_conversation_renderer",["jquery","core/notification","core/str","core/templates","core/user_date","core_message/message_drawer_view_conversation_constants","core/aria","core/truncate"],(function($,Notification,Str,Templates,UserDate,Constants,Aria,Truncate){var SELECTORS=Constants.SELECTORS,TEMPLATES=Constants.TEMPLATES,CONVERSATION_TYPES=Constants.CONVERSATION_TYPES,getMessagesContainer=function(body){return body.find(SELECTORS.CONTENT_MESSAGES_CONTAINER)},getSelfConversationMessageContainer=function(body){return body.find(SELECTORS.SELF_CONVERSATION_MESSAGE_CONTAINER)},getContactRequestSentContainer=function(body){return body.find(SELECTORS.CONTACT_REQUEST_SENT_MESSAGE_CONTAINER)},getFooterContentContainer=function(footer){return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_CONTAINER)},getFooterEditModeContainer=function(footer){return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER)},getFooterPlaceholderContainer=function(footer){return footer.find(SELECTORS.PLACEHOLDER_CONTAINER)},showFooterPlaceholder=function(footer){getFooterPlaceholderContainer(footer).removeClass("hidden")},getFooterRequireContactContainer=function(footer){return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER)},getFooterRequireUnblockContainer=function(footer){return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER)},getFooterUnableToMessageContainer=function(footer){return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER)},hideAllFooterElements=function(footer){!function(footer){getFooterContentContainer(footer).addClass("hidden")}(footer),function(footer){getFooterEditModeContainer(footer).addClass("hidden")}(footer),function(footer){getFooterPlaceholderContainer(footer).addClass("hidden")}(footer),function(footer){getFooterRequireContactContainer(footer).addClass("hidden")}(footer),function(footer){getFooterRequireUnblockContainer(footer).addClass("hidden")}(footer),function(footer){getFooterUnableToMessageContainer(footer).addClass("hidden")}(footer)},getContentPlaceholderContainer=function(body){return body.find(SELECTORS.CONTENT_PLACEHOLDER_CONTAINER)},getHeaderContent=function(header){return header.find(SELECTORS.HEADER)},showHeaderContent=function(header){getHeaderContent(header).removeClass("hidden")},hideHeaderContent=function(header){getHeaderContent(header).addClass("hidden")},getHeaderEditMode=function(header){return header.find(SELECTORS.HEADER_EDIT_MODE)},hideHeaderEditMode=function(header){getHeaderEditMode(header).addClass("hidden")},getHeaderPlaceholderContainer=function(header){return header.find(SELECTORS.HEADER_PLACEHOLDER_CONTAINER)},showHeaderPlaceholder=function(header){getHeaderPlaceholderContainer(header).removeClass("hidden")},hideHeaderPlaceholder=function(header){getHeaderPlaceholderContainer(header).addClass("hidden")},getMessageElement=function(body,messageId){return getMessagesContainer(body).find('[data-message-id="'+messageId+'"]')},getDayElement=function(body,dayTimeCreated){return getMessagesContainer(body).find('[data-day-id="'+dayTimeCreated+'"]')},getMoreMessagesLoadingIconContainer=function(body){return body.find(SELECTORS.MORE_MESSAGES_LOADING_ICON_CONTAINER)},getConfirmDialogueContainer=function(root){return root.find(SELECTORS.CONFIRM_DIALOGUE_CONTAINER)},showConfirmDialogueContainer=function(root){var container=getConfirmDialogueContainer(root),siblings=container.siblings(":not(.hidden)");Aria.hide(siblings.get()),siblings.attr("data-confirm-dialogue-hidden",!0),container.removeClass("hidden")},hideConfirmDialogueContainer=function(root){var container=getConfirmDialogueContainer(root),siblings=container.siblings('[data-confirm-dialogue-hidden="true"]');Aria.unhide(siblings.get()),siblings.removeAttr("data-confirm-dialogue-hidden"),container.addClass("hidden")},formatMessagesForTemplate=function(messages,datesCache){return messages.map((function(message){return{id:message.id,isread:message.isRead,fromloggedinuser:message.fromLoggedInUser,userfrom:message.userFrom,text:message.text,formattedtime:message.timeCreated?datesCache[message.timeCreated]:null}}))},renderConversation=function(header,body,footer,data){var renderingPromises=[],hasAddDays=data.days.add.length>0,hasAddMessages=data.messages.add.length>0,hasUpdateMessages=data.messages.update.length>0,timestampsToFormat=[],datesCachePromise=$.Deferred().resolve({}).promise();return hasAddDays&&(timestampsToFormat=timestampsToFormat.concat(data.days.add.reduce((function(carry,day){return carry.concat(day.value.messages.reduce((function(timestamps,message){return message.timeCreated&×tamps.push(message.timeCreated),timestamps}),[]))}),[]))),hasAddMessages&&(timestampsToFormat=timestampsToFormat.concat(data.messages.add.reduce((function(timestamps,message){return message.value.timeCreated&×tamps.push(message.value.timeCreated),timestamps}),[]))),hasUpdateMessages&&(timestampsToFormat=timestampsToFormat.concat(data.messages.update.reduce((function(timestamps,message){return message.before.timeCreated!=message.after.timeCreated&×tamps.push(message.after.timeCreated),timestamps}),[]))),timestampsToFormat.length&&(datesCachePromise=Str.get_string("strftimetime24","core_langconfig").then((function(format){var requests=timestampsToFormat.map((function(timestamp){return{timestamp:timestamp,format:format}}));return UserDate.get(requests)})).then((function(formattedTimes){return timestampsToFormat.reduce((function(carry,timestamp,index){return carry[timestamp]=formattedTimes[index],carry}),{})}))),hasAddDays&&renderingPromises.push(datesCachePromise.then((function(datesCache){return function(header,body,footer,days,datesCache){var messagesContainer=getMessagesContainer(body),daysRenderPromises=days.map((function(data){var timestampDate=new Date(1e3*data.value.timestamp);return Templates.render(TEMPLATES.DAY,{timestamp:data.value.timestamp,currentyear:timestampDate.getFullYear()===(new Date).getFullYear(),messages:formatMessagesForTemplate(data.value.messages,datesCache)})}));return $.when.apply($,daysRenderPromises).then((function(){days.forEach((function(data,index){daysRenderPromises[index].then((function(html){if(data.before){var element=getDayElement(body,data.before.timestamp);return $(html).insertBefore(element)}return messagesContainer.append(html)})).catch((function(){}))}))}))}(0,body,0,data.days.add,datesCache)}))),hasAddMessages&&renderingPromises.push(datesCachePromise.then((function(datesCache){return function(header,body,footer,messages,datesCache){var messagesData=messages.map((function(data){return data.value})),formattedMessages=formatMessagesForTemplate(messagesData,datesCache);return Templates.render(TEMPLATES.MESSAGES,{messages:formattedMessages}).then((function(html){var messageList=$(html);messages.forEach((function(data){var messageHtml=messageList.find('[data-message-id="'+data.value.id+'"]');if(data.before){var element=getMessageElement(body,data.before.id);return messageHtml.insertBefore(element)}return getDayElement(body,data.day.timestamp).find(SELECTORS.DAY_MESSAGES_CONTAINER).append(messageHtml)}))}))}(0,body,0,data.messages.add,datesCache)}))),hasUpdateMessages&&renderingPromises.push(datesCachePromise.then((function(datesCache){return function(header,body,footer,messages,datesCache){messages.forEach((function(message){var before=message.before,after=message.after,element=getMessageElement(body,before.id);if(before.id!=after.id&&element.attr("data-message-id",after.id),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),time=element.find(SELECTORS.TIME_CREATED),retry=element.find(SELECTORS.RETRY_SEND);switch(loading.addClass("hidden"),Aria.hide(loading.get()),time.addClass("hidden"),Aria.hide(time.get()),retry.addClass("hidden"),Aria.hide(retry.get()),element.removeClass("border border-danger"),after.sendState){case"pending":loading.removeClass("hidden"),Aria.unhide(loading.get());break;case"error":retry.removeClass("hidden"),Aria.unhide(retry.get()),element.addClass("border border-danger");break;case"sent":time.removeClass("hidden"),Aria.unhide(time.get())}}if(before.text!=after.text&&element.find(SELECTORS.TEXT_CONTAINER).html(after.text),before.errorMessage!=after.errorMessage){var messageContainer=element.find(SELECTORS.ERROR_MESSAGE_CONTAINER);message=messageContainer.find(SELECTORS.ERROR_MESSAGE),after.errorMessage?(messageContainer.removeClass("hidden"),Aria.unhide(messageContainer.get()),message.text(after.errorMessage)):(messageContainer.addClass("hidden"),Aria.unhide(messageContainer.get()),message.text(""))}}))}(0,body,0,data.messages.update,datesCache)}))),data.days.remove.length>0&&function(body,days){days.forEach((function(data){getDayElement(body,data.timestamp).remove()}))}(body,data.days.remove),data.messages.remove.length>0&&function(body,messages){messages.forEach((function(data){getMessageElement(body,data.id).remove()}))}(body,data.messages.remove),$.when.apply($,renderingPromises)},renderHeader=function(header,body,footer,data){var headerContainer=getHeaderContent(header),template=TEMPLATES.HEADER_PUBLIC;return data.context.showrouteback="false"===header.attr("data-from-panel"),data.type==CONVERSATION_TYPES.PRIVATE?template=data.showControls?TEMPLATES.HEADER_PRIVATE:TEMPLATES.HEADER_PRIVATE_NO_CONTROLS:data.type==CONVERSATION_TYPES.SELF&&(template=TEMPLATES.HEADER_SELF),Templates.render(template,data.context).then((function(html,js){Templates.replaceNodeContents(headerContainer,html,js)}))},renderFooter=function(header,body,footer,data){switch(hideAllFooterElements(footer),data.type){case"placeholder":return showFooterPlaceholder(footer);case"add-contact":return Str.get_strings([{key:"requirecontacttomessage",component:"core_message",param:data.user.fullname},{key:"isnotinyourcontacts",component:"core_message",param:data.user.fullname}]).then((function(strings){var title=strings[1],text=strings[0],footerContainer=getFooterRequireContactContainer(footer);return footerContainer.find(SELECTORS.TITLE).text(title),footerContainer.find(SELECTORS.TEXT).text(text),function(footer){getFooterRequireContactContainer(footer).removeClass("hidden")}(footer),strings}));case"edit-mode":return function(footer){getFooterEditModeContainer(footer).removeClass("hidden")}(footer);case"content":return function(footer){getFooterContentContainer(footer).removeClass("hidden")}(footer);case"unblock":return function(footer){getFooterRequireUnblockContainer(footer).removeClass("hidden")}(footer);case"unable-to-message":return function(footer){getFooterUnableToMessageContainer(footer).removeClass("hidden")}(footer)}return!0},renderScrollToMessage=function(header,body,footer,messageId){var messagesContainer=getMessagesContainer(body),position=getMessageElement(body,messageId).position();if(position){var scrollTop=messagesContainer.scrollTop()+position.top;messagesContainer.scrollTop(scrollTop)}},renderLoadingMembers=function(header,body,footer,isLoadingMembers){isLoadingMembers?(hideHeaderContent(header),showHeaderPlaceholder(header)):(showHeaderContent(header),hideHeaderPlaceholder(header))},renderLoadingFirstMessages=function(header,body,footer,isLoadingFirstMessages){isLoadingFirstMessages?(function(body){getMessagesContainer(body).addClass("hidden")}(body),function(body){getContentPlaceholderContainer(body).removeClass("hidden")}(body)):(function(body){getMessagesContainer(body).removeClass("hidden")}(body),function(body){getContentPlaceholderContainer(body).addClass("hidden")}(body))},renderLoadingMessages=function(header,body,footer,isLoading){isLoading?function(body){getMoreMessagesLoadingIconContainer(body).removeClass("hidden")}(body):function(body){getMoreMessagesLoadingIconContainer(body).addClass("hidden")}(body)},renderShowEmojiPicker=function(header,body,footer,show){var container=function(footer){return footer.find(SELECTORS.EMOJI_PICKER_CONTAINER)}(footer);show?(container.removeClass("hidden"),Aria.unhide(container.get()),container.find(SELECTORS.EMOJI_PICKER_SEARCH_INPUT).focus()):(container.addClass("hidden"),Aria.hide(container.get()))},renderShowEmojiAutoComplete=function(header,body,footer,show){var container=function(footer){return footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER)}(footer);show?(container.removeClass("hidden"),Aria.unhide(container.get())):(container.addClass("hidden"),Aria.hide(container.get()))},showConfirmDialogue=function(header,body,footer,buttonSelectors,bodyText,headerText,canCancel,skipHeader,showOk){var dialogue=getConfirmDialogueContainer(body),buttons=buttonSelectors.map((function(selector){return dialogue.find(selector)})),cancelButton=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_CANCEL_BUTTON),okayButton=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_OKAY_BUTTON),text=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_TEXT),dialogueHeader=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_HEADER);(dialogue.find("button").addClass("hidden"),canCancel?cancelButton.removeClass("hidden"):cancelButton.addClass("hidden"),showOk?okayButton.removeClass("hidden"):okayButton.addClass("hidden"),headerText)?((dialogueHeader=$('

    ')).text(headerText),dialogue.find(SELECTORS.CONFIRM_DIALOGUE).prepend(dialogueHeader)):dialogueHeader.length&&dialogueHeader.remove();buttons.forEach((function(button){button.removeClass("hidden")})),text.text(bodyText),showConfirmDialogueContainer(footer),showConfirmDialogueContainer(body),skipHeader||showConfirmDialogueContainer(header),dialogue.find(SELECTORS.CAN_RECEIVE_FOCUS).filter(":visible").first().focus()},hideConfirmDialogue=function(header,body,footer){var dialogue=getConfirmDialogueContainer(body),cancelButton=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_CANCEL_BUTTON),okayButton=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_OKAY_BUTTON),text=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_TEXT),dialogueHeader=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_HEADER);return hideCheckDeleteDialogue(body),hideConfirmDialogueContainer(body),hideConfirmDialogueContainer(footer),hideConfirmDialogueContainer(header),dialogue.find("button").addClass("hidden"),cancelButton.removeClass("hidden"),okayButton.removeClass("hidden"),text.text(""),dialogueHeader.length&&dialogueHeader.remove(),header.find(SELECTORS.CAN_RECEIVE_FOCUS).first().focus(),!0},renderConfirmBlockUser=function(header,body,footer,user){if(user){const username=truncateUsername(user.fullname);return user.canmessageevenifblocked?Str.get_string("cantblockuser","core_message",username).then((function(string){return showConfirmDialogue(header,body,footer,[],string,"",!1,!1,!0)})):Str.get_string("blockuserconfirm","core_message",username).then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_BLOCK],string,"",!0,!1)}))}return hideConfirmDialogue(header,body,footer)},renderConfirmUnblockUser=function(header,body,footer,user){return user?Str.get_string("unblockuserconfirm","core_message",truncateUsername(user.fullname)).then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_UNBLOCK],string,"",!0,!1)})):hideConfirmDialogue(header,body,footer)},renderConfirmAddContact=function(header,body,footer,user){if(user){const userFullName=Truncate.truncate(user.fullname,{length:30,words:!0,ellipsis:"..."});return Str.get_string("addcontactconfirm","core_message",userFullName).then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_ADD_CONTACT],string,"",!0,!1)}))}return hideConfirmDialogue(header,body,footer)},renderConfirmRemoveContact=function(header,body,footer,user){return user?Str.get_string("removecontactconfirm","core_message",truncateUsername(user.fullname)).then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT],string,"",!0,!1)})):hideConfirmDialogue(header,body,footer)},renderConfirmDeleteSelectedMessages=function(header,body,footer,data){var showmessage=null;return data.type==CONVERSATION_TYPES.SELF?showmessage="deleteselectedmessagesconfirmselfconversation":data.canDeleteMessagesForAllUsers?(showCheckDeleteDialogue(body),showmessage="deleteforeveryoneselectedmessagesconfirm"):showmessage="deleteselectedmessagesconfirm",data.show?Str.get_string(showmessage,"core_message").then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES],string,"",!0,!1)})):hideConfirmDialogue(header,body,footer)},renderConfirmDeleteConversation=function(header,body,footer,type){var showmessage=null;return type==CONVERSATION_TYPES.SELF?showmessage="deleteallselfconfirm":type&&(showmessage="deleteallconfirm"),showmessage?Str.get_string(showmessage,"core_message").then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION],string,"",!0,!1)})):hideConfirmDialogue(header,body,footer)},renderConfirmContactRequest=function(header,body,footer,user){return user?Str.get_string("userwouldliketocontactyou","core_message",truncateUsername(user.fullname)).then((function(string){var buttonSelectors=[SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST,SELECTORS.ACTION_DECLINE_CONTACT_REQUEST];return showConfirmDialogue(header,body,footer,buttonSelectors,string,"",!1,!0)})):hideConfirmDialogue(header,body,footer)},showCheckDeleteDialogue=function(body){getConfirmDialogueContainer(body).find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER).removeClass("hidden")},hideCheckDeleteDialogue=function(body){var dialogue=getConfirmDialogueContainer(body),checkboxRegion=dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER);dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE).prop("checked",!1),checkboxRegion.addClass("hidden")},renderIsBlocked=function(header,body,footer,isBlocked){isBlocked?(header.find(SELECTORS.ACTION_REQUEST_BLOCK).addClass("hidden"),header.find(SELECTORS.ACTION_REQUEST_UNBLOCK).removeClass("hidden")):(header.find(SELECTORS.ACTION_REQUEST_BLOCK).removeClass("hidden"),header.find(SELECTORS.ACTION_REQUEST_UNBLOCK).addClass("hidden"))},renderIsFavourite=function(header,body,footer,state){var favouriteIcon=header.find(SELECTORS.FAVOURITE_ICON_CONTAINER),addFavourite=header.find(SELECTORS.ACTION_CONFIRM_FAVOURITE),removeFavourite=header.find(SELECTORS.ACTION_CONFIRM_UNFAVOURITE);switch(state){case"hide":favouriteIcon.addClass("hidden"),addFavourite.addClass("hidden"),removeFavourite.addClass("hidden");break;case"show-add":favouriteIcon.addClass("hidden"),addFavourite.removeClass("hidden"),removeFavourite.addClass("hidden");break;case"show-remove":favouriteIcon.removeClass("hidden"),addFavourite.addClass("hidden"),removeFavourite.removeClass("hidden")}},renderIsMuted=function(header,body,footer,state){var muteIcon=header.find(SELECTORS.MUTED_ICON_CONTAINER),setMuted=header.find(SELECTORS.ACTION_CONFIRM_MUTE),unsetMuted=header.find(SELECTORS.ACTION_CONFIRM_UNMUTE);switch(state){case"hide":muteIcon.addClass("hidden"),setMuted.addClass("hidden"),unsetMuted.addClass("hidden");break;case"show-mute":muteIcon.addClass("hidden"),setMuted.removeClass("hidden"),unsetMuted.addClass("hidden");break;case"show-unmute":muteIcon.removeClass("hidden"),setMuted.addClass("hidden"),unsetMuted.removeClass("hidden")}},renderIsContact=function(header,body,footer,state){var addContact=header.find(SELECTORS.ACTION_REQUEST_ADD_CONTACT),removeContact=header.find(SELECTORS.ACTION_REQUEST_REMOVE_CONTACT);switch(state){case"pending-contact":addContact.addClass("hidden"),removeContact.addClass("hidden");break;case"contact":addContact.addClass("hidden"),removeContact.removeClass("hidden");break;case"non-contact":addContact.removeClass("hidden"),removeContact.addClass("hidden")}},renderLoadingConfirmAction=function(header,body,footer,isLoading){var dialogue=getConfirmDialogueContainer(body),buttons=dialogue.find("button"),buttonText=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_BUTTON_TEXT),loadingIcon=dialogue.find(SELECTORS.LOADING_ICON_CONTAINER);isLoading?(buttons.prop("disabled",!0),buttonText.addClass("hidden"),loadingIcon.removeClass("hidden")):(buttons.prop("disabled",!1),buttonText.removeClass("hidden"),loadingIcon.addClass("hidden"))},renderInEditMode=function(header,body,footer,inEditMode){var messages=null;inEditMode?((messages=body.find(SELECTORS.MESSAGE_NOT_SELECTED)).find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).removeClass("hidden"),hideHeaderContent(header),function(header){getHeaderEditMode(header).removeClass("hidden")}(header)):((messages=getMessagesContainer(body)).find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).addClass("hidden"),messages.find(SELECTORS.MESSAGE_SELECTED_ICON).addClass("hidden"),showHeaderContent(header),hideHeaderEditMode(header))},renderSelectedMessages=function(header,body,footer,data){var hasSelectedMessages=data.count>0;data.add.length&&data.add.forEach((function(messageId){var message=getMessageElement(body,messageId);message.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).addClass("hidden"),message.find(SELECTORS.MESSAGE_SELECTED_ICON).removeClass("hidden"),message.attr("aria-checked",!0)})),data.remove.length&&data.remove.forEach((function(messageId){var message=getMessageElement(body,messageId);hasSelectedMessages&&message.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).removeClass("hidden"),message.find(SELECTORS.MESSAGE_SELECTED_ICON).addClass("hidden"),message.attr("aria-checked",!1)})),function(header,value){getHeaderEditMode(header).find(SELECTORS.MESSAGES_SELECTED_COUNT).text(value)}(header,data.count)},renderRequireAddContact=function(header,body,footer,data){return data.show&&!data.hasMessages?Str.get_strings([{key:"requirecontacttomessage",component:"core_message",param:data.user.fullname},{key:"isnotinyourcontacts",component:"core_message",param:data.user.fullname}]).then((function(strings){var title=strings[1],text=strings[0];return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_REQUEST_ADD_CONTACT],text,title,!1,!0)})):hideConfirmDialogue(header,body,footer)},renderSelfConversationMessage=function(header,body,footer,displayMessage){var container=getSelfConversationMessageContainer(body);return displayMessage?container.removeClass("hidden"):container.addClass("hidden"),!0},renderContactRequestSent=function(header,body,footer,userFullName){var container=getContactRequestSentContainer(body);return userFullName?Str.get_string("yourcontactrequestpending","core_message",truncateUsername(userFullName)).then((function(string){return container.find(SELECTORS.TEXT).text(string),container.removeClass("hidden"),string})):(container.addClass("hidden"),!0)},renderReset=function(header,body,footer){return hideConfirmDialogue(header,body,footer),function(body){getContactRequestSentContainer(body).addClass("hidden")}(body),function(body){getSelfConversationMessageContainer(body).addClass("hidden")}(body),function(header){hideHeaderContent(header),hideHeaderEditMode(header),hideHeaderPlaceholder(header)}(header),showHeaderPlaceholder(header),hideAllFooterElements(footer),showFooterPlaceholder(footer),!0},truncateUsername=function(username){return Truncate.truncate(username,{length:30,words:!0,ellipsis:"..."})};return{render:function(header,body,footer,patch){var configs=[{reset:renderReset},{conversation:renderConversation,header:renderHeader,footer:renderFooter,confirmBlockUser:renderConfirmBlockUser,confirmUnblockUser:renderConfirmUnblockUser,confirmAddContact:renderConfirmAddContact,confirmRemoveContact:renderConfirmRemoveContact,confirmDeleteSelectedMessages:renderConfirmDeleteSelectedMessages,confirmDeleteConversation:renderConfirmDeleteConversation,confirmContactRequest:renderConfirmContactRequest,requireAddContact:renderRequireAddContact,selfConversationMessage:renderSelfConversationMessage,contactRequestSent:renderContactRequestSent},{loadingMembers:renderLoadingMembers,loadingFirstMessages:renderLoadingFirstMessages,loadingMessages:renderLoadingMessages,isBlocked:renderIsBlocked,isContact:renderIsContact,isFavourite:renderIsFavourite,isMuted:renderIsMuted,loadingConfirmAction:renderLoadingConfirmAction,inEditMode:renderInEditMode,showEmojiPicker:renderShowEmojiPicker,showEmojiAutoComplete:renderShowEmojiAutoComplete},{scrollToMessage:renderScrollToMessage,selectedMessages:renderSelectedMessages}],processConfig=function(config){var results=[];for(var key in patch)if(config.hasOwnProperty(key)){var renderFunc=config[key],patchValue=patch[key];results.push(renderFunc(header,body,footer,patchValue))}return results},renderingPromises=processConfig(configs[0]);return renderingPromises=renderingPromises.concat(processConfig(configs[1])),$.when.apply($,renderingPromises).then((function(){for(var i=2;ibody.find(SELECTORS.UNABLE_TO_MESSAGE_CONTAINER),getMessageTextArea=footer=>footer.find(SELECTORS.MESSAGE_TEXT_AREA),getSendMessageButton=footer=>footer.find(SELECTORS.SEND_MESSAGE_BUTTON),getEmojiPickerButton=footer=>footer.find(SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON);var getContactRequestSentContainer=function(body){return body.find(SELECTORS.CONTACT_REQUEST_SENT_MESSAGE_CONTAINER)},getFooterContentContainer=function(footer){return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_CONTAINER)},getFooterEditModeContainer=function(footer){return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER)},getFooterPlaceholderContainer=function(footer){return footer.find(SELECTORS.PLACEHOLDER_CONTAINER)},showFooterPlaceholder=function(footer){getFooterPlaceholderContainer(footer).removeClass("hidden")},getFooterRequireContactContainer=function(footer){return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER)},getFooterRequireUnblockContainer=function(footer){return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER)},hideAllFooterElements=function(footer){!function(footer){getFooterContentContainer(footer).addClass("hidden")}(footer),function(footer){getFooterEditModeContainer(footer).addClass("hidden")}(footer),function(footer){getFooterPlaceholderContainer(footer).addClass("hidden")}(footer),function(footer){getFooterRequireContactContainer(footer).addClass("hidden")}(footer),function(footer){getFooterRequireUnblockContainer(footer).addClass("hidden")}(footer)},getContentPlaceholderContainer=function(body){return body.find(SELECTORS.CONTENT_PLACEHOLDER_CONTAINER)},getHeaderContent=function(header){return header.find(SELECTORS.HEADER)},showHeaderContent=function(header){getHeaderContent(header).removeClass("hidden")},hideHeaderContent=function(header){getHeaderContent(header).addClass("hidden")};const hideUnableToMessage=body=>{getUnableToMessageContainer(body).addClass("hidden")},enableAllFooterContentElements=footer=>{(footer=>{getEmojiPickerButton(footer).prop("disabled",!1)})(footer),(footer=>{getSendMessageButton(footer).prop("disabled",!1)})(footer),(footer=>{const messageTextArea=getMessageTextArea(footer);messageTextArea.prop("disabled",!1),Aria.unhide(messageTextArea.get()),Str.get_string("writeamessage","core_message").done((function(string){messageTextArea.prop("disabled")||messageTextArea.prop("placeholder",string)}))})(footer)},disableAllFooterContentElements=footer=>{(footer=>{getEmojiPickerButton(footer).prop("disabled",!0)})(footer),(footer=>{getSendMessageButton(footer).prop("disabled",!0)})(footer),(footer=>{const messageTextArea=getMessageTextArea(footer);messageTextArea.prop("disabled",!0),messageTextArea.removeAttr("placeholder"),Aria.unhide(messageTextArea.get())})(footer)};var getHeaderEditMode=function(header){return header.find(SELECTORS.HEADER_EDIT_MODE)},hideHeaderEditMode=function(header){getHeaderEditMode(header).addClass("hidden")},getHeaderPlaceholderContainer=function(header){return header.find(SELECTORS.HEADER_PLACEHOLDER_CONTAINER)},showHeaderPlaceholder=function(header){getHeaderPlaceholderContainer(header).removeClass("hidden")},hideHeaderPlaceholder=function(header){getHeaderPlaceholderContainer(header).addClass("hidden")},getMessageElement=function(body,messageId){return getMessagesContainer(body).find('[data-message-id="'+messageId+'"]')},getDayElement=function(body,dayTimeCreated){return getMessagesContainer(body).find('[data-day-id="'+dayTimeCreated+'"]')},getMoreMessagesLoadingIconContainer=function(body){return body.find(SELECTORS.MORE_MESSAGES_LOADING_ICON_CONTAINER)},getConfirmDialogueContainer=function(root){return root.find(SELECTORS.CONFIRM_DIALOGUE_CONTAINER)},showConfirmDialogueContainer=function(root){var container=getConfirmDialogueContainer(root),siblings=container.siblings(":not(.hidden)");Aria.hide(siblings.get()),siblings.attr("data-confirm-dialogue-hidden",!0),container.removeClass("hidden")},hideConfirmDialogueContainer=function(root){var container=getConfirmDialogueContainer(root),siblings=container.siblings('[data-confirm-dialogue-hidden="true"]');Aria.unhide(siblings.get()),siblings.removeAttr("data-confirm-dialogue-hidden"),container.addClass("hidden")},formatMessagesForTemplate=function(messages,datesCache){return messages.map((function(message){return{id:message.id,isread:message.isRead,fromloggedinuser:message.fromLoggedInUser,userfrom:message.userFrom,text:message.text,formattedtime:message.timeCreated?datesCache[message.timeCreated]:null}}))},renderConversation=function(header,body,footer,data){var renderingPromises=[],hasAddDays=data.days.add.length>0,hasAddMessages=data.messages.add.length>0,hasUpdateMessages=data.messages.update.length>0,timestampsToFormat=[],datesCachePromise=$.Deferred().resolve({}).promise();return hasAddDays&&(timestampsToFormat=timestampsToFormat.concat(data.days.add.reduce((function(carry,day){return carry.concat(day.value.messages.reduce((function(timestamps,message){return message.timeCreated&×tamps.push(message.timeCreated),timestamps}),[]))}),[]))),hasAddMessages&&(timestampsToFormat=timestampsToFormat.concat(data.messages.add.reduce((function(timestamps,message){return message.value.timeCreated&×tamps.push(message.value.timeCreated),timestamps}),[]))),hasUpdateMessages&&(timestampsToFormat=timestampsToFormat.concat(data.messages.update.reduce((function(timestamps,message){return message.before.timeCreated!=message.after.timeCreated&×tamps.push(message.after.timeCreated),timestamps}),[]))),timestampsToFormat.length&&(datesCachePromise=Str.get_string("strftimetime24","core_langconfig").then((function(format){var requests=timestampsToFormat.map((function(timestamp){return{timestamp:timestamp,format:format}}));return UserDate.get(requests)})).then((function(formattedTimes){return timestampsToFormat.reduce((function(carry,timestamp,index){return carry[timestamp]=formattedTimes[index],carry}),{})}))),hasAddDays&&renderingPromises.push(datesCachePromise.then((function(datesCache){return function(header,body,footer,days,datesCache,isAbleToMessage){var messagesContainer=getMessagesContainer(body),daysRenderPromises=days.map((function(data){var timestampDate=new Date(1e3*data.value.timestamp);return Templates.render(TEMPLATES.DAY,{timestamp:data.value.timestamp,currentyear:timestampDate.getFullYear()===(new Date).getFullYear(),messages:formatMessagesForTemplate(data.value.messages,datesCache)})}));return $.when.apply($,daysRenderPromises).then((function(){days.forEach((function(data,index){daysRenderPromises[index].then((function(html){if(data.before){var element=getDayElement(body,data.before.timestamp);return $(html).insertBefore(element)}return messagesContainer.append(html)})).always((function(){const showUnableToMessage=!isAbleToMessage&&index===days.length-1;messagesContainer.find(SELECTORS.DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER).last().toggleClass("hidden",!showUnableToMessage),hideUnableToMessage(body)})).catch((function(){}))}))}))}(0,body,0,data.days.add,datesCache,!data.unableToMessage)}))),hasAddMessages&&renderingPromises.push(datesCachePromise.then((function(datesCache){return function(header,body,footer,messages,datesCache){var messagesData=messages.map((function(data){return data.value})),formattedMessages=formatMessagesForTemplate(messagesData,datesCache);return Templates.render(TEMPLATES.MESSAGES,{messages:formattedMessages}).then((function(html){var messageList=$(html);messages.forEach((function(data){var messageHtml=messageList.find('[data-message-id="'+data.value.id+'"]');if(data.before){var element=getMessageElement(body,data.before.id);return messageHtml.insertBefore(element)}return getDayElement(body,data.day.timestamp).find(SELECTORS.DAY_MESSAGES_CONTAINER).append(messageHtml)}))}))}(0,body,0,data.messages.add,datesCache)}))),hasUpdateMessages&&renderingPromises.push(datesCachePromise.then((function(datesCache){return function(header,body,footer,messages,datesCache){messages.forEach((function(message){var before=message.before,after=message.after,element=getMessageElement(body,before.id);if(before.id!=after.id&&element.attr("data-message-id",after.id),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),time=element.find(SELECTORS.TIME_CREATED),retry=element.find(SELECTORS.RETRY_SEND);switch(loading.addClass("hidden"),Aria.hide(loading.get()),time.addClass("hidden"),Aria.hide(time.get()),retry.addClass("hidden"),Aria.hide(retry.get()),element.removeClass("border border-danger"),after.sendState){case"pending":loading.removeClass("hidden"),Aria.unhide(loading.get());break;case"error":retry.removeClass("hidden"),Aria.unhide(retry.get()),element.addClass("border border-danger");break;case"sent":time.removeClass("hidden"),Aria.unhide(time.get())}}if(before.text!=after.text&&element.find(SELECTORS.TEXT_CONTAINER).html(after.text),before.errorMessage!=after.errorMessage){var messageContainer=element.find(SELECTORS.ERROR_MESSAGE_CONTAINER);message=messageContainer.find(SELECTORS.ERROR_MESSAGE),after.errorMessage?(messageContainer.removeClass("hidden"),Aria.unhide(messageContainer.get()),message.text(after.errorMessage)):(messageContainer.addClass("hidden"),Aria.unhide(messageContainer.get()),message.text(""))}}))}(0,body,0,data.messages.update,datesCache)}))),data.days.remove.length>0&&function(body,days){days.forEach((function(data){getDayElement(body,data.timestamp).remove()}))}(body,data.days.remove),data.messages.remove.length>0&&function(body,messages){messages.forEach((function(data){getMessageElement(body,data.id).remove()}))}(body,data.messages.remove),$.when.apply($,renderingPromises)},renderHeader=function(header,body,footer,data){var headerContainer=getHeaderContent(header),template=TEMPLATES.HEADER_PUBLIC;return data.context.showrouteback="false"===header.attr("data-from-panel"),data.type==CONVERSATION_TYPES.PRIVATE?template=data.showControls?TEMPLATES.HEADER_PRIVATE:TEMPLATES.HEADER_PRIVATE_NO_CONTROLS:data.type==CONVERSATION_TYPES.SELF&&(template=TEMPLATES.HEADER_SELF),Templates.render(template,data.context).then((function(html,js){Templates.replaceNodeContents(headerContainer,html,js)}))},renderFooter=function(header,body,footer,data){switch(hideAllFooterElements(footer),data.type){case"placeholder":return showFooterPlaceholder(footer);case"add-contact":return Str.get_strings([{key:"requirecontacttomessage",component:"core_message",param:data.user.fullname},{key:"isnotinyourcontacts",component:"core_message",param:data.user.fullname}]).then((function(strings){var title=strings[1],text=strings[0],footerContainer=getFooterRequireContactContainer(footer);return footerContainer.find(SELECTORS.TITLE).text(title),footerContainer.find(SELECTORS.TEXT).text(text),function(footer){getFooterRequireContactContainer(footer).removeClass("hidden")}(footer),strings}));case"edit-mode":return function(footer){getFooterEditModeContainer(footer).removeClass("hidden")}(footer);case"content":return function(footer){getFooterContentContainer(footer).removeClass("hidden")}(footer);case"unblock":return function(footer){getFooterRequireUnblockContainer(footer).removeClass("hidden")}(footer)}return!0},renderScrollToMessage=function(header,body,footer,messageId){var messagesContainer=getMessagesContainer(body),position=getMessageElement(body,messageId).position();if(position){var scrollTop=messagesContainer.scrollTop()+position.top;messagesContainer.scrollTop(scrollTop)}},renderLoadingMembers=function(header,body,footer,isLoadingMembers){isLoadingMembers?(hideHeaderContent(header),showHeaderPlaceholder(header)):(showHeaderContent(header),hideHeaderPlaceholder(header))},renderLoadingFirstMessages=function(header,body,footer,isLoadingFirstMessages){isLoadingFirstMessages?(function(body){getMessagesContainer(body).addClass("hidden")}(body),function(body){getContentPlaceholderContainer(body).removeClass("hidden")}(body)):(function(body){getMessagesContainer(body).removeClass("hidden")}(body),function(body){getContentPlaceholderContainer(body).addClass("hidden")}(body))},renderLoadingMessages=function(header,body,footer,isLoading){isLoading?function(body){getMoreMessagesLoadingIconContainer(body).removeClass("hidden")}(body):function(body){getMoreMessagesLoadingIconContainer(body).addClass("hidden")}(body)},renderShowEmojiPicker=function(header,body,footer,show){var container=function(footer){return footer.find(SELECTORS.EMOJI_PICKER_CONTAINER)}(footer);show?(container.removeClass("hidden"),Aria.unhide(container.get()),container.find(SELECTORS.EMOJI_PICKER_SEARCH_INPUT).focus()):(container.addClass("hidden"),Aria.hide(container.get()))},renderShowEmojiAutoComplete=function(header,body,footer,show){var container=function(footer){return footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER)}(footer);show?(container.removeClass("hidden"),Aria.unhide(container.get())):(container.addClass("hidden"),Aria.hide(container.get()))},showConfirmDialogue=function(header,body,footer,buttonSelectors,bodyText,headerText,canCancel,skipHeader,showOk){var dialogue=getConfirmDialogueContainer(body),buttons=buttonSelectors.map((function(selector){return dialogue.find(selector)})),cancelButton=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_CANCEL_BUTTON),okayButton=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_OKAY_BUTTON),text=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_TEXT),dialogueHeader=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_HEADER);(dialogue.find("button").addClass("hidden"),canCancel?cancelButton.removeClass("hidden"):cancelButton.addClass("hidden"),showOk?okayButton.removeClass("hidden"):okayButton.addClass("hidden"),headerText)?((dialogueHeader=$('

    ')).text(headerText),dialogue.find(SELECTORS.CONFIRM_DIALOGUE).prepend(dialogueHeader)):dialogueHeader.length&&dialogueHeader.remove();buttons.forEach((function(button){button.removeClass("hidden")})),text.text(bodyText),showConfirmDialogueContainer(footer),showConfirmDialogueContainer(body),skipHeader||showConfirmDialogueContainer(header),dialogue.find(SELECTORS.CAN_RECEIVE_FOCUS).filter(":visible").first().focus()},hideConfirmDialogue=function(header,body,footer){var dialogue=getConfirmDialogueContainer(body),cancelButton=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_CANCEL_BUTTON),okayButton=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_OKAY_BUTTON),text=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_TEXT),dialogueHeader=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_HEADER);return hideCheckDeleteDialogue(body),hideConfirmDialogueContainer(body),hideConfirmDialogueContainer(footer),hideConfirmDialogueContainer(header),dialogue.find("button").addClass("hidden"),cancelButton.removeClass("hidden"),okayButton.removeClass("hidden"),text.text(""),dialogueHeader.length&&dialogueHeader.remove(),header.find(SELECTORS.CAN_RECEIVE_FOCUS).first().focus(),!0},renderConfirmBlockUser=function(header,body,footer,user){if(user){const username=truncateUsername(user.fullname);return user.canmessageevenifblocked?Str.get_string("cantblockuser","core_message",username).then((function(string){return showConfirmDialogue(header,body,footer,[],string,"",!1,!1,!0)})):Str.get_string("blockuserconfirm","core_message",username).then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_BLOCK],string,"",!0,!1)}))}return hideConfirmDialogue(header,body,footer)},renderConfirmUnblockUser=function(header,body,footer,user){return user?Str.get_string("unblockuserconfirm","core_message",truncateUsername(user.fullname)).then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_UNBLOCK],string,"",!0,!1)})):hideConfirmDialogue(header,body,footer)},renderConfirmAddContact=function(header,body,footer,user){if(user){const userFullName=Truncate.truncate(user.fullname,{length:30,words:!0,ellipsis:"..."});return Str.get_string("addcontactconfirm","core_message",userFullName).then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_ADD_CONTACT],string,"",!0,!1)}))}return hideConfirmDialogue(header,body,footer)},renderConfirmRemoveContact=function(header,body,footer,user){return user?Str.get_string("removecontactconfirm","core_message",truncateUsername(user.fullname)).then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT],string,"",!0,!1)})):hideConfirmDialogue(header,body,footer)},renderConfirmDeleteSelectedMessages=function(header,body,footer,data){var showmessage=null;return data.type==CONVERSATION_TYPES.SELF?showmessage="deleteselectedmessagesconfirmselfconversation":data.canDeleteMessagesForAllUsers?(showCheckDeleteDialogue(body),showmessage="deleteforeveryoneselectedmessagesconfirm"):showmessage="deleteselectedmessagesconfirm",data.show?Str.get_string(showmessage,"core_message").then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES],string,"",!0,!1)})):hideConfirmDialogue(header,body,footer)},renderConfirmDeleteConversation=function(header,body,footer,type){var showmessage=null;return type==CONVERSATION_TYPES.SELF?showmessage="deleteallselfconfirm":type&&(showmessage="deleteallconfirm"),showmessage?Str.get_string(showmessage,"core_message").then((function(string){return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION],string,"",!0,!1)})):hideConfirmDialogue(header,body,footer)},renderConfirmContactRequest=function(header,body,footer,user){return user?Str.get_string("userwouldliketocontactyou","core_message",truncateUsername(user.fullname)).then((function(string){var buttonSelectors=[SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST,SELECTORS.ACTION_DECLINE_CONTACT_REQUEST];return showConfirmDialogue(header,body,footer,buttonSelectors,string,"",!1,!0)})):hideConfirmDialogue(header,body,footer)},showCheckDeleteDialogue=function(body){getConfirmDialogueContainer(body).find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER).removeClass("hidden")},hideCheckDeleteDialogue=function(body){var dialogue=getConfirmDialogueContainer(body),checkboxRegion=dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER);dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE).prop("checked",!1),checkboxRegion.addClass("hidden")},renderIsBlocked=function(header,body,footer,isBlocked){isBlocked?(header.find(SELECTORS.ACTION_REQUEST_BLOCK).addClass("hidden"),header.find(SELECTORS.ACTION_REQUEST_UNBLOCK).removeClass("hidden")):(header.find(SELECTORS.ACTION_REQUEST_BLOCK).removeClass("hidden"),header.find(SELECTORS.ACTION_REQUEST_UNBLOCK).addClass("hidden"))},renderIsFavourite=function(header,body,footer,state){var favouriteIcon=header.find(SELECTORS.FAVOURITE_ICON_CONTAINER),addFavourite=header.find(SELECTORS.ACTION_CONFIRM_FAVOURITE),removeFavourite=header.find(SELECTORS.ACTION_CONFIRM_UNFAVOURITE);switch(state){case"hide":favouriteIcon.addClass("hidden"),addFavourite.addClass("hidden"),removeFavourite.addClass("hidden");break;case"show-add":favouriteIcon.addClass("hidden"),addFavourite.removeClass("hidden"),removeFavourite.addClass("hidden");break;case"show-remove":favouriteIcon.removeClass("hidden"),addFavourite.addClass("hidden"),removeFavourite.removeClass("hidden")}},renderIsMuted=function(header,body,footer,state){var muteIcon=header.find(SELECTORS.MUTED_ICON_CONTAINER),setMuted=header.find(SELECTORS.ACTION_CONFIRM_MUTE),unsetMuted=header.find(SELECTORS.ACTION_CONFIRM_UNMUTE);switch(state){case"hide":muteIcon.addClass("hidden"),setMuted.addClass("hidden"),unsetMuted.addClass("hidden");break;case"show-mute":muteIcon.addClass("hidden"),setMuted.removeClass("hidden"),unsetMuted.addClass("hidden");break;case"show-unmute":muteIcon.removeClass("hidden"),setMuted.addClass("hidden"),unsetMuted.removeClass("hidden")}},renderIsContact=function(header,body,footer,state){var addContact=header.find(SELECTORS.ACTION_REQUEST_ADD_CONTACT),removeContact=header.find(SELECTORS.ACTION_REQUEST_REMOVE_CONTACT);switch(state){case"pending-contact":addContact.addClass("hidden"),removeContact.addClass("hidden");break;case"contact":addContact.addClass("hidden"),removeContact.removeClass("hidden");break;case"non-contact":addContact.removeClass("hidden"),removeContact.addClass("hidden")}},renderLoadingConfirmAction=function(header,body,footer,isLoading){var dialogue=getConfirmDialogueContainer(body),buttons=dialogue.find("button"),buttonText=dialogue.find(SELECTORS.CONFIRM_DIALOGUE_BUTTON_TEXT),loadingIcon=dialogue.find(SELECTORS.LOADING_ICON_CONTAINER);isLoading?(buttons.prop("disabled",!0),buttonText.addClass("hidden"),loadingIcon.removeClass("hidden")):(buttons.prop("disabled",!1),buttonText.removeClass("hidden"),loadingIcon.addClass("hidden"))},renderInEditMode=function(header,body,footer,inEditMode){var messages=null;inEditMode?((messages=body.find(SELECTORS.MESSAGE_NOT_SELECTED)).find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).removeClass("hidden"),hideHeaderContent(header),function(header){getHeaderEditMode(header).removeClass("hidden")}(header)):((messages=getMessagesContainer(body)).find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).addClass("hidden"),messages.find(SELECTORS.MESSAGE_SELECTED_ICON).addClass("hidden"),showHeaderContent(header),hideHeaderEditMode(header))},renderSelectedMessages=function(header,body,footer,data){var hasSelectedMessages=data.count>0;data.add.length&&data.add.forEach((function(messageId){var message=getMessageElement(body,messageId);message.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).addClass("hidden"),message.find(SELECTORS.MESSAGE_SELECTED_ICON).removeClass("hidden"),message.attr("aria-checked",!0)})),data.remove.length&&data.remove.forEach((function(messageId){var message=getMessageElement(body,messageId);hasSelectedMessages&&message.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).removeClass("hidden"),message.find(SELECTORS.MESSAGE_SELECTED_ICON).addClass("hidden"),message.attr("aria-checked",!1)})),function(header,value){getHeaderEditMode(header).find(SELECTORS.MESSAGES_SELECTED_COUNT).text(value)}(header,data.count)},renderRequireAddContact=function(header,body,footer,data){return data.show&&!data.hasMessages?Str.get_strings([{key:"requirecontacttomessage",component:"core_message",param:data.user.fullname},{key:"isnotinyourcontacts",component:"core_message",param:data.user.fullname}]).then((function(strings){var title=strings[1],text=strings[0];return showConfirmDialogue(header,body,footer,[SELECTORS.ACTION_REQUEST_ADD_CONTACT],text,title,!1,!0)})):hideConfirmDialogue(header,body,footer)},renderSelfConversationMessage=function(header,body,footer,displayMessage){var container=getSelfConversationMessageContainer(body);return displayMessage?container.removeClass("hidden"):container.addClass("hidden"),!0},renderUnableToMessage=function(header,body,footer,disable){return disable?(disableAllFooterContentElements(footer),body.find(SELECTORS.DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER).length||(body=>{getUnableToMessageContainer(body).removeClass("hidden")})(body)):(enableAllFooterContentElements(footer),hideUnableToMessage(body)),!0},renderContactRequestSent=function(header,body,footer,userFullName){var container=getContactRequestSentContainer(body);return userFullName?Str.get_string("yourcontactrequestpending","core_message",truncateUsername(userFullName)).then((function(string){return container.find(SELECTORS.TEXT).text(string),container.removeClass("hidden"),string})):(container.addClass("hidden"),!0)},renderReset=function(header,body,footer){return hideConfirmDialogue(header,body,footer),function(body){getContactRequestSentContainer(body).addClass("hidden")}(body),function(body){getSelfConversationMessageContainer(body).addClass("hidden")}(body),function(header){hideHeaderContent(header),hideHeaderEditMode(header),hideHeaderPlaceholder(header)}(header),showHeaderPlaceholder(header),hideAllFooterElements(footer),showFooterPlaceholder(footer),enableAllFooterContentElements(footer),hideUnableToMessage(body),!0},truncateUsername=function(username){return Truncate.truncate(username,{length:30,words:!0,ellipsis:"..."})};return{render:function(header,body,footer,patch){var configs=[{reset:renderReset},{conversation:renderConversation,header:renderHeader,footer:renderFooter,confirmBlockUser:renderConfirmBlockUser,confirmUnblockUser:renderConfirmUnblockUser,confirmAddContact:renderConfirmAddContact,confirmRemoveContact:renderConfirmRemoveContact,confirmDeleteSelectedMessages:renderConfirmDeleteSelectedMessages,confirmDeleteConversation:renderConfirmDeleteConversation,confirmContactRequest:renderConfirmContactRequest,requireAddContact:renderRequireAddContact,selfConversationMessage:renderSelfConversationMessage,contactRequestSent:renderContactRequestSent,unableToMessage:renderUnableToMessage},{loadingMembers:renderLoadingMembers,loadingFirstMessages:renderLoadingFirstMessages,loadingMessages:renderLoadingMessages,isBlocked:renderIsBlocked,isContact:renderIsContact,isFavourite:renderIsFavourite,isMuted:renderIsMuted,loadingConfirmAction:renderLoadingConfirmAction,inEditMode:renderInEditMode,showEmojiPicker:renderShowEmojiPicker,showEmojiAutoComplete:renderShowEmojiAutoComplete},{scrollToMessage:renderScrollToMessage,selectedMessages:renderSelectedMessages}],processConfig=function(config){var results=[];for(var key in patch)if(config.hasOwnProperty(key)){var renderFunc=config[key],patchValue=patch[key];results.push(renderFunc(header,body,footer,patchValue))}return results},renderingPromises=processConfig(configs[0]);return renderingPromises=renderingPromises.concat(processConfig(configs[1])),$.when.apply($,renderingPromises).then((function(){for(var i=2;i.\n\n/**\n * This module updates the UI for the conversation page in the message\n * drawer.\n *\n * The module will take a patch from the message_drawer_view_conversation_patcher\n * module and update the UI to reflect the changes.\n *\n * This is the only module that ever modifies the UI of the conversation page.\n *\n * @module core_message/message_drawer_view_conversation_renderer\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/str',\n 'core/templates',\n 'core/user_date',\n 'core_message/message_drawer_view_conversation_constants',\n 'core/aria',\n 'core/truncate',\n],\nfunction(\n $,\n Notification,\n Str,\n Templates,\n UserDate,\n Constants,\n Aria,\n Truncate,\n) {\n var SELECTORS = Constants.SELECTORS;\n var TEMPLATES = Constants.TEMPLATES;\n var CONVERSATION_TYPES = Constants.CONVERSATION_TYPES;\n\n /**\n * Get the messages container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var getMessagesContainer = function(body) {\n return body.find(SELECTORS.CONTENT_MESSAGES_CONTAINER);\n };\n\n /**\n * Show the messages container element.\n *\n * @param {Object} body Conversation body container element.\n */\n var showMessagesContainer = function(body) {\n getMessagesContainer(body).removeClass('hidden');\n };\n\n /**\n * Hide the messages container element.\n *\n * @param {Object} body Conversation body container element.\n */\n var hideMessagesContainer = function(body) {\n getMessagesContainer(body).addClass('hidden');\n };\n\n /**\n * Get the self-conversation message container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var getSelfConversationMessageContainer = function(body) {\n return body.find(SELECTORS.SELF_CONVERSATION_MESSAGE_CONTAINER);\n };\n\n /**\n * Hide the self-conversation message container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var hideSelfConversationMessageContainer = function(body) {\n return getSelfConversationMessageContainer(body).addClass('hidden');\n };\n\n /**\n * Get the contact request sent container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var getContactRequestSentContainer = function(body) {\n return body.find(SELECTORS.CONTACT_REQUEST_SENT_MESSAGE_CONTAINER);\n };\n\n /**\n * Hide the contact request sent container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var hideContactRequestSentContainer = function(body) {\n return getContactRequestSentContainer(body).addClass('hidden');\n };\n\n /**\n * Get the footer container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer container element.\n */\n var getFooterContentContainer = function(footer) {\n return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_CONTAINER);\n };\n\n /**\n * Show the footer container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterContent = function(footer) {\n getFooterContentContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterContent = function(footer) {\n getFooterContentContainer(footer).addClass('hidden');\n };\n\n /**\n * Get the footer edit mode container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer container element.\n */\n var getFooterEditModeContainer = function(footer) {\n return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER);\n };\n\n /**\n * Show the footer edit mode container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterEditMode = function(footer) {\n getFooterEditModeContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer edit mode container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterEditMode = function(footer) {\n getFooterEditModeContainer(footer).addClass('hidden');\n };\n\n /**\n * Get the footer placeholder.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer placeholder container element.\n */\n var getFooterPlaceholderContainer = function(footer) {\n return footer.find(SELECTORS.PLACEHOLDER_CONTAINER);\n };\n\n /**\n * Show the footer placeholder\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterPlaceholder = function(footer) {\n getFooterPlaceholderContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer placeholder\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterPlaceholder = function(footer) {\n getFooterPlaceholderContainer(footer).addClass('hidden');\n };\n\n /**\n * Get the footer Require add as contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer Require add as contact container element.\n */\n var getFooterRequireContactContainer = function(footer) {\n return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER);\n };\n\n /**\n * Show the footer add as contact dialogue.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterRequireContact = function(footer) {\n getFooterRequireContactContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer add as contact dialogue.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterRequireContact = function(footer) {\n getFooterRequireContactContainer(footer).addClass('hidden');\n };\n\n /**\n * Get the footer Required to unblock contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer Required to unblock contact container element.\n */\n var getFooterRequireUnblockContainer = function(footer) {\n return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER);\n };\n\n /**\n * Show the footer Required to unblock contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterRequireUnblock = function(footer) {\n getFooterRequireUnblockContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer Required to unblock contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterRequireUnblock = function(footer) {\n getFooterRequireUnblockContainer(footer).addClass('hidden');\n };\n\n /**\n * Get the footer Unable to message contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer Unable to message contact container element.\n */\n var getFooterUnableToMessageContainer = function(footer) {\n return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER);\n };\n\n /**\n * Show the footer Unable to message contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterUnableToMessage = function(footer) {\n getFooterUnableToMessageContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer Unable to message contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterUnableToMessage = function(footer) {\n getFooterUnableToMessageContainer(footer).addClass('hidden');\n };\n\n /**\n * Hide all header elements.\n *\n * @param {Object} header Conversation header container element.\n */\n var hideAllHeaderElements = function(header) {\n hideHeaderContent(header);\n hideHeaderEditMode(header);\n hideHeaderPlaceholder(header);\n };\n\n /**\n * Hide all footer dialogues and messages.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideAllFooterElements = function(footer) {\n hideFooterContent(footer);\n hideFooterEditMode(footer);\n hideFooterPlaceholder(footer);\n hideFooterRequireContact(footer);\n hideFooterRequireUnblock(footer);\n hideFooterUnableToMessage(footer);\n };\n\n /**\n * Get the content placeholder container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The body placeholder container element.\n */\n var getContentPlaceholderContainer = function(body) {\n return body.find(SELECTORS.CONTENT_PLACEHOLDER_CONTAINER);\n };\n\n /**\n * Show the content placeholder.\n *\n * @param {Object} body Conversation body container element.\n */\n var showContentPlaceholder = function(body) {\n getContentPlaceholderContainer(body).removeClass('hidden');\n };\n\n /**\n * Hide the content placeholder.\n *\n * @param {Object} body Conversation body container element.\n */\n var hideContentPlaceholder = function(body) {\n getContentPlaceholderContainer(body).addClass('hidden');\n };\n\n /**\n * Get the header content container element.\n *\n * @param {Object} header Conversation header container element.\n * @return {Object} The header content container element.\n */\n var getHeaderContent = function(header) {\n return header.find(SELECTORS.HEADER);\n };\n\n /**\n * Show the header content.\n *\n * @param {Object} header Conversation header container element.\n */\n var showHeaderContent = function(header) {\n getHeaderContent(header).removeClass('hidden');\n };\n\n /**\n * Hide the header content.\n *\n * @param {Object} header Conversation header container element.\n */\n var hideHeaderContent = function(header) {\n getHeaderContent(header).addClass('hidden');\n };\n\n /**\n * Get the header edit mode container element.\n *\n * @param {Object} header Conversation header container element.\n * @return {Object} The header content container element.\n */\n var getHeaderEditMode = function(header) {\n return header.find(SELECTORS.HEADER_EDIT_MODE);\n };\n\n /**\n * Show the header edit mode container.\n *\n * @param {Object} header Conversation header container element.\n */\n var showHeaderEditMode = function(header) {\n getHeaderEditMode(header).removeClass('hidden');\n };\n\n /**\n * Hide the header edit mode container.\n *\n * @param {Object} header Conversation header container element.\n */\n var hideHeaderEditMode = function(header) {\n getHeaderEditMode(header).addClass('hidden');\n };\n\n /**\n * Get the header placeholder container element.\n *\n * @param {Object} header Conversation header container element.\n * @return {Object} The header placeholder container element.\n */\n var getHeaderPlaceholderContainer = function(header) {\n return header.find(SELECTORS.HEADER_PLACEHOLDER_CONTAINER);\n };\n\n /**\n * Show the header placeholder.\n *\n * @param {Object} header Conversation header container element.\n */\n var showHeaderPlaceholder = function(header) {\n getHeaderPlaceholderContainer(header).removeClass('hidden');\n };\n\n /**\n * Hide the header placeholder.\n *\n * @param {Object} header Conversation header container element.\n */\n var hideHeaderPlaceholder = function(header) {\n getHeaderPlaceholderContainer(header).addClass('hidden');\n };\n\n /**\n * Get the emoji picker container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The emoji picker container element.\n */\n var getEmojiPickerContainer = function(footer) {\n return footer.find(SELECTORS.EMOJI_PICKER_CONTAINER);\n };\n\n /**\n * Get the emoji picker container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The emoji picker container element.\n */\n var getEmojiAutoCompleteContainer = function(footer) {\n return footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER);\n };\n\n /**\n * Get a message element.\n *\n * @param {Object} body Conversation body container element.\n * @param {Number} messageId the Message id.\n * @return {Object} A message element from the conversation.\n */\n var getMessageElement = function(body, messageId) {\n var messagesContainer = getMessagesContainer(body);\n return messagesContainer.find('[data-message-id=\"' + messageId + '\"]');\n };\n\n /**\n * Get the day container element. The day container element holds a list of messages for that day.\n *\n * @param {Object} body Conversation body container element.\n * @param {Number} dayTimeCreated Midnight timestamp for the day.\n * @return {Object} jQuery object\n */\n var getDayElement = function(body, dayTimeCreated) {\n var messagesContainer = getMessagesContainer(body);\n return messagesContainer.find('[data-day-id=\"' + dayTimeCreated + '\"]');\n };\n\n /**\n * Get the more messages loading icon container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The more messages loading container element.\n */\n var getMoreMessagesLoadingIconContainer = function(body) {\n return body.find(SELECTORS.MORE_MESSAGES_LOADING_ICON_CONTAINER);\n };\n\n /**\n * Show the more messages loading icon.\n *\n * @param {Object} body Conversation body container element.\n */\n var showMoreMessagesLoadingIcon = function(body) {\n getMoreMessagesLoadingIconContainer(body).removeClass('hidden');\n };\n\n /**\n * Hide the more messages loading icon.\n *\n * @param {Object} body Conversation body container element.\n */\n var hideMoreMessagesLoadingIcon = function(body) {\n getMoreMessagesLoadingIconContainer(body).addClass('hidden');\n };\n\n /**\n * Get the confirm dialogue container element.\n *\n * @param {Object} root The container element to search.\n * @return {Object} The confirm dialogue container element.\n */\n var getConfirmDialogueContainer = function(root) {\n return root.find(SELECTORS.CONFIRM_DIALOGUE_CONTAINER);\n };\n\n /**\n * Show the confirm dialogue container element.\n *\n * @param {Object} root The container element containing a dialogue.\n */\n var showConfirmDialogueContainer = function(root) {\n var container = getConfirmDialogueContainer(root);\n var siblings = container.siblings(':not(.hidden)');\n Aria.hide(siblings.get());\n siblings.attr('data-confirm-dialogue-hidden', true);\n\n container.removeClass('hidden');\n };\n\n /**\n * Hide the confirm dialogue container element.\n *\n * @param {Object} root The container element containing a dialogue.\n */\n var hideConfirmDialogueContainer = function(root) {\n var container = getConfirmDialogueContainer(root);\n var siblings = container.siblings('[data-confirm-dialogue-hidden=\"true\"]');\n Aria.unhide(siblings.get());\n siblings.removeAttr('data-confirm-dialogue-hidden');\n\n container.addClass('hidden');\n };\n\n /**\n * Set the number of selected messages.\n *\n * @param {Object} header The header container element.\n * @param {Number} value The new number to display.\n */\n var setMessagesSelectedCount = function(header, value) {\n getHeaderEditMode(header).find(SELECTORS.MESSAGES_SELECTED_COUNT).text(value);\n };\n\n /**\n * Format message for the mustache template, transform camelCase properties to lowercase properties.\n *\n * @param {Array} messages Array of message objects.\n * @param {Object} datesCache Cache timestamps and their formatted date string.\n * @return {Array} Messages formated for mustache template.\n */\n var formatMessagesForTemplate = function(messages, datesCache) {\n return messages.map(function(message) {\n return {\n id: message.id,\n isread: message.isRead,\n fromloggedinuser: message.fromLoggedInUser,\n userfrom: message.userFrom,\n text: message.text,\n formattedtime: message.timeCreated ? datesCache[message.timeCreated] : null\n };\n });\n };\n\n /**\n * Create rendering promises for each day containing messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Array} days Array of days containing messages.\n * @param {Object} datesCache Cache timestamps and their formatted date string.\n * @return {Promise} Days rendering promises.\n */\n var renderAddDays = function(header, body, footer, days, datesCache) {\n var messagesContainer = getMessagesContainer(body);\n var daysRenderPromises = days.map(function(data) {\n var timestampDate = new Date(data.value.timestamp * 1000);\n return Templates.render(TEMPLATES.DAY, {\n timestamp: data.value.timestamp,\n currentyear: timestampDate.getFullYear() === (new Date()).getFullYear(),\n messages: formatMessagesForTemplate(data.value.messages, datesCache)\n });\n });\n\n return $.when.apply($, daysRenderPromises).then(function() {\n // Wait until all of the rendering is done for each of the days\n // to ensure they are added to the page in the correct order.\n days.forEach(function(data, index) {\n daysRenderPromises[index]\n .then(function(html) {\n if (data.before) {\n var element = getDayElement(body, data.before.timestamp);\n return $(html).insertBefore(element);\n } else {\n return messagesContainer.append(html);\n }\n })\n .catch(function() {\n // Fail silently.\n });\n });\n\n return;\n });\n };\n\n /**\n * Add (more) messages to day containers.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Array} messages List of messages.\n * @param {Object} datesCache Cache timestamps and their formatted date string.\n * @return {Promise} Messages rendering promises.\n */\n var renderAddMessages = function(header, body, footer, messages, datesCache) {\n var messagesData = messages.map(function(data) {\n return data.value;\n });\n var formattedMessages = formatMessagesForTemplate(messagesData, datesCache);\n\n return Templates.render(TEMPLATES.MESSAGES, {messages: formattedMessages})\n .then(function(html) {\n var messageList = $(html);\n messages.forEach(function(data) {\n var messageHtml = messageList.find('[data-message-id=\"' + data.value.id + '\"]');\n if (data.before) {\n var element = getMessageElement(body, data.before.id);\n return messageHtml.insertBefore(element);\n } else {\n var dayContainer = getDayElement(body, data.day.timestamp);\n var dayMessagesContainer = dayContainer.find(SELECTORS.DAY_MESSAGES_CONTAINER);\n return dayMessagesContainer.append(messageHtml);\n }\n });\n\n return;\n });\n };\n\n /**\n * Update existing messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Array} messages List of messages.\n * @param {Object} datesCache Cache timestamps and their formatted date string.\n */\n var renderUpdateMessages = function(header, body, footer, messages, datesCache) {\n messages.forEach(function(message) {\n var before = message.before;\n var after = message.after;\n var element = getMessageElement(body, before.id);\n\n if (before.id != after.id) {\n element.attr('data-message-id', after.id);\n }\n\n if (before.timeCreated != after.timeCreated) {\n var formattedTime = datesCache[after.timeCreated];\n element.find(SELECTORS.LOADING_ICON_CONTAINER).addClass('hidden');\n element.find(SELECTORS.TIME_CREATED).text(formattedTime).removeClass('hidden');\n }\n\n if (before.sendState != after.sendState) {\n var loading = element.find(SELECTORS.LOADING_ICON_CONTAINER);\n var time = element.find(SELECTORS.TIME_CREATED);\n var retry = element.find(SELECTORS.RETRY_SEND);\n\n loading.addClass('hidden');\n Aria.hide(loading.get());\n\n time.addClass('hidden');\n Aria.hide(time.get());\n\n retry.addClass('hidden');\n Aria.hide(retry.get());\n\n element.removeClass('border border-danger');\n\n switch (after.sendState) {\n case 'pending':\n loading.removeClass('hidden');\n Aria.unhide(loading.get());\n break;\n case 'error':\n retry.removeClass('hidden');\n Aria.unhide(retry.get());\n element.addClass('border border-danger');\n break;\n case 'sent':\n time.removeClass('hidden');\n Aria.unhide(time.get());\n break;\n }\n }\n\n if (before.text != after.text) {\n element.find(SELECTORS.TEXT_CONTAINER).html(after.text);\n }\n\n if (before.errorMessage != after.errorMessage) {\n var messageContainer = element.find(SELECTORS.ERROR_MESSAGE_CONTAINER);\n var message = messageContainer.find(SELECTORS.ERROR_MESSAGE);\n\n if (after.errorMessage) {\n messageContainer.removeClass('hidden');\n Aria.unhide(messageContainer.get());\n message.text(after.errorMessage);\n } else {\n messageContainer.addClass('hidden');\n Aria.unhide(messageContainer.get());\n message.text('');\n }\n }\n });\n };\n\n /**\n * Remove days from conversation.\n *\n * @param {Object} body The body container element.\n * @param {Array} days Array of days to be removed.\n */\n var renderRemoveDays = function(body, days) {\n days.forEach(function(data) {\n getDayElement(body, data.timestamp).remove();\n });\n };\n\n /**\n * Remove messages from conversation.\n *\n * @param {Object} body The body container element.\n * @param {Array} messages Array of messages to be removed.\n */\n var renderRemoveMessages = function(body, messages) {\n messages.forEach(function(data) {\n getMessageElement(body, data.id).remove();\n });\n };\n\n /**\n * Render the full conversation base on input from the statemanager.\n *\n * This will pre-load all of the formatted timestamps for each message that\n * needs to render to reduce the number of networks requests.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data The conversation diff.\n * @return {Object} jQuery promise.\n */\n var renderConversation = function(header, body, footer, data) {\n var renderingPromises = [];\n var hasAddDays = data.days.add.length > 0;\n var hasAddMessages = data.messages.add.length > 0;\n var hasUpdateMessages = data.messages.update.length > 0;\n var timestampsToFormat = [];\n var datesCachePromise = $.Deferred().resolve({}).promise();\n\n if (hasAddDays) {\n // Search for all of the timeCreated values in all of the messages in all of\n // the days that we need to render.\n timestampsToFormat = timestampsToFormat.concat(data.days.add.reduce(function(carry, day) {\n return carry.concat(day.value.messages.reduce(function(timestamps, message) {\n if (message.timeCreated) {\n timestamps.push(message.timeCreated);\n }\n return timestamps;\n }, []));\n }, []));\n }\n\n if (hasAddMessages) {\n // Search for all of the timeCreated values in all of the messages that we\n // need to render.\n timestampsToFormat = timestampsToFormat.concat(data.messages.add.reduce(function(timestamps, message) {\n if (message.value.timeCreated) {\n timestamps.push(message.value.timeCreated);\n }\n return timestamps;\n }, []));\n }\n\n if (hasUpdateMessages) {\n timestampsToFormat = timestampsToFormat.concat(data.messages.update.reduce(function(timestamps, message) {\n if (message.before.timeCreated != message.after.timeCreated) {\n timestamps.push(message.after.timeCreated);\n }\n return timestamps;\n }, []));\n }\n\n if (timestampsToFormat.length) {\n // If we have timestamps then pre-load the formatted version of each of them\n // in a single request to the server. This saves the templates doing multiple\n // individual requests.\n datesCachePromise = Str.get_string('strftimetime24', 'core_langconfig')\n .then(function(format) {\n var requests = timestampsToFormat.map(function(timestamp) {\n return {\n timestamp: timestamp,\n format: format\n };\n });\n\n return UserDate.get(requests);\n })\n .then(function(formattedTimes) {\n return timestampsToFormat.reduce(function(carry, timestamp, index) {\n carry[timestamp] = formattedTimes[index];\n return carry;\n }, {});\n });\n }\n\n if (hasAddDays) {\n renderingPromises.push(datesCachePromise.then(function(datesCache) {\n return renderAddDays(header, body, footer, data.days.add, datesCache);\n }));\n }\n\n if (hasAddMessages) {\n renderingPromises.push(datesCachePromise.then(function(datesCache) {\n return renderAddMessages(header, body, footer, data.messages.add, datesCache);\n }));\n }\n\n if (hasUpdateMessages) {\n renderingPromises.push(datesCachePromise.then(function(datesCache) {\n return renderUpdateMessages(header, body, footer, data.messages.update, datesCache);\n }));\n }\n\n if (data.days.remove.length > 0) {\n renderRemoveDays(body, data.days.remove);\n }\n\n if (data.messages.remove.length > 0) {\n renderRemoveMessages(body, data.messages.remove);\n }\n\n return $.when.apply($, renderingPromises);\n };\n\n /**\n * Render the conversation header.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data Data for header.\n * @return {Object} jQuery promise\n */\n var renderHeader = function(header, body, footer, data) {\n var headerContainer = getHeaderContent(header);\n var template = TEMPLATES.HEADER_PUBLIC;\n data.context.showrouteback = (header.attr('data-from-panel') === \"false\");\n if (data.type == CONVERSATION_TYPES.PRIVATE) {\n template = data.showControls ? TEMPLATES.HEADER_PRIVATE : TEMPLATES.HEADER_PRIVATE_NO_CONTROLS;\n } else if (data.type == CONVERSATION_TYPES.SELF) {\n template = TEMPLATES.HEADER_SELF;\n }\n\n return Templates.render(template, data.context)\n .then(function(html, js) {\n Templates.replaceNodeContents(headerContainer, html, js);\n return;\n });\n };\n\n /**\n * Render the conversation footer.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data Data for footer.\n * @return {Object} jQuery promise.\n */\n var renderFooter = function(header, body, footer, data) {\n hideAllFooterElements(footer);\n\n switch (data.type) {\n case 'placeholder':\n return showFooterPlaceholder(footer);\n case 'add-contact':\n return Str.get_strings([\n {\n key: 'requirecontacttomessage',\n component: 'core_message',\n param: data.user.fullname\n },\n {\n key: 'isnotinyourcontacts',\n component: 'core_message',\n param: data.user.fullname\n }\n ])\n .then(function(strings) {\n var title = strings[1];\n var text = strings[0];\n var footerContainer = getFooterRequireContactContainer(footer);\n footerContainer.find(SELECTORS.TITLE).text(title);\n footerContainer.find(SELECTORS.TEXT).text(text);\n showFooterRequireContact(footer);\n return strings;\n });\n case 'edit-mode':\n return showFooterEditMode(footer);\n case 'content':\n return showFooterContent(footer);\n case 'unblock':\n return showFooterRequireUnblock(footer);\n case 'unable-to-message':\n return showFooterUnableToMessage(footer);\n }\n\n return true;\n };\n\n /**\n * Scroll to a message in the conversation.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Number} messageId Message id.\n */\n var renderScrollToMessage = function(header, body, footer, messageId) {\n var messagesContainer = getMessagesContainer(body);\n var messageElement = getMessageElement(body, messageId);\n var position = messageElement.position();\n // Scroll the message container down to the top of the message element.\n if (position) {\n var scrollTop = messagesContainer.scrollTop() + position.top;\n messagesContainer.scrollTop(scrollTop);\n }\n };\n\n /**\n * Hide or show the conversation header.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isLoadingMembers Members loading.\n */\n var renderLoadingMembers = function(header, body, footer, isLoadingMembers) {\n if (isLoadingMembers) {\n hideHeaderContent(header);\n showHeaderPlaceholder(header);\n } else {\n showHeaderContent(header);\n hideHeaderPlaceholder(header);\n }\n };\n\n /**\n * Hide or show loading conversation messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isLoadingFirstMessages Messages loading.\n */\n var renderLoadingFirstMessages = function(header, body, footer, isLoadingFirstMessages) {\n if (isLoadingFirstMessages) {\n hideMessagesContainer(body);\n showContentPlaceholder(body);\n } else {\n showMessagesContainer(body);\n hideContentPlaceholder(body);\n }\n };\n\n /**\n * Hide or show loading more messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isLoading Messages loading.\n */\n var renderLoadingMessages = function(header, body, footer, isLoading) {\n if (isLoading) {\n showMoreMessagesLoadingIcon(body);\n } else {\n hideMoreMessagesLoadingIcon(body);\n }\n };\n\n /**\n * Hide or show the emoji picker.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} show Should the emoji picker be visible.\n */\n var renderShowEmojiPicker = function(header, body, footer, show) {\n var container = getEmojiPickerContainer(footer);\n\n if (show) {\n container.removeClass('hidden');\n Aria.unhide(container.get());\n container.find(SELECTORS.EMOJI_PICKER_SEARCH_INPUT).focus();\n } else {\n container.addClass('hidden');\n Aria.hide(container.get());\n }\n };\n\n /**\n * Hide or show the emoji auto complete.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} show Should the emoji picker be visible.\n */\n var renderShowEmojiAutoComplete = function(header, body, footer, show) {\n var container = getEmojiAutoCompleteContainer(footer);\n\n if (show) {\n container.removeClass('hidden');\n Aria.unhide(container.get());\n } else {\n container.addClass('hidden');\n Aria.hide(container.get());\n }\n };\n\n /**\n * Show a confirmation dialogue\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {String} buttonSelectors Selectors for the buttons to show.\n * @param {String} bodyText Text to show in dialogue.\n * @param {String} headerText Text to show in dialogue header.\n * @param {Bool} canCancel Can this dialogue be cancelled.\n * @param {Bool} skipHeader Skip blanking out the header\n * @param {Bool} showOk Show an 'Okay' button for a dialogue which will close it\n */\n var showConfirmDialogue = function(\n header,\n body,\n footer,\n buttonSelectors,\n bodyText,\n headerText,\n canCancel,\n skipHeader,\n showOk\n ) {\n var dialogue = getConfirmDialogueContainer(body);\n var buttons = buttonSelectors.map(function(selector) {\n return dialogue.find(selector);\n });\n var cancelButton = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_CANCEL_BUTTON);\n var okayButton = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_OKAY_BUTTON);\n var text = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_TEXT);\n var dialogueHeader = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_HEADER);\n\n dialogue.find('button').addClass('hidden');\n\n if (canCancel) {\n cancelButton.removeClass('hidden');\n } else {\n cancelButton.addClass('hidden');\n }\n\n if (showOk) {\n okayButton.removeClass('hidden');\n } else {\n okayButton.addClass('hidden');\n }\n\n if (headerText) {\n // Create the dialogue header.\n dialogueHeader = $('

    ');\n dialogueHeader.text(headerText);\n // Prepend it to the confirmation body.\n var confirmDialogue = dialogue.find(SELECTORS.CONFIRM_DIALOGUE);\n confirmDialogue.prepend(dialogueHeader);\n } else if (dialogueHeader.length) {\n // Header text is empty but dialogue header is present, so remove it.\n dialogueHeader.remove();\n }\n\n buttons.forEach(function(button) {\n button.removeClass('hidden');\n });\n text.text(bodyText);\n showConfirmDialogueContainer(footer);\n showConfirmDialogueContainer(body);\n\n if (!skipHeader) {\n showConfirmDialogueContainer(header);\n }\n\n dialogue.find(SELECTORS.CAN_RECEIVE_FOCUS).filter(':visible').first().focus();\n };\n\n /**\n * Hide the dialogue\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @return {Bool} always true.\n */\n var hideConfirmDialogue = function(header, body, footer) {\n var dialogue = getConfirmDialogueContainer(body);\n var cancelButton = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_CANCEL_BUTTON);\n var okayButton = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_OKAY_BUTTON);\n var text = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_TEXT);\n var dialogueHeader = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_HEADER);\n\n hideCheckDeleteDialogue(body);\n hideConfirmDialogueContainer(body);\n hideConfirmDialogueContainer(footer);\n hideConfirmDialogueContainer(header);\n dialogue.find('button').addClass('hidden');\n cancelButton.removeClass('hidden');\n okayButton.removeClass('hidden');\n text.text('');\n\n // Remove dialogue header if present.\n if (dialogueHeader.length) {\n dialogueHeader.remove();\n }\n\n header.find(SELECTORS.CAN_RECEIVE_FOCUS).first().focus();\n return true;\n };\n\n /**\n * Render the confirm block user dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user User to block.\n * @return {Object} jQuery promise\n */\n var renderConfirmBlockUser = function(header, body, footer, user) {\n if (user) {\n const username = truncateUsername(user.fullname);\n if (user.canmessageevenifblocked) {\n return Str.get_string('cantblockuser', 'core_message', username)\n .then(function(string) {\n return showConfirmDialogue(header, body, footer, [], string, '', false, false, true);\n });\n } else {\n return Str.get_string('blockuserconfirm', 'core_message', username)\n .then(function(string) {\n return showConfirmDialogue(header, body, footer, [SELECTORS.ACTION_CONFIRM_BLOCK], string, '', true, false);\n });\n }\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the confirm unblock user dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user User to unblock.\n * @return {Object} jQuery promise\n */\n var renderConfirmUnblockUser = function(header, body, footer, user) {\n if (user) {\n return Str.get_string('unblockuserconfirm', 'core_message', truncateUsername(user.fullname))\n .then(function(string) {\n return showConfirmDialogue(header, body, footer, [SELECTORS.ACTION_CONFIRM_UNBLOCK], string, '', true, false);\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the add user as contact dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user User to add as contact.\n * @return {Object} jQuery promise\n */\n var renderConfirmAddContact = function(header, body, footer, user) {\n if (user) {\n // Truncate long usernames.\n const userFullName = Truncate.truncate(user.fullname, {\n length: 30,\n words: true,\n ellipsis: '...'\n });\n return Str.get_string('addcontactconfirm', 'core_message', userFullName)\n .then(function(string) {\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_CONFIRM_ADD_CONTACT],\n string,\n '',\n true,\n false\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the remove user from contacts dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user User to remove from contacts.\n * @return {Object} jQuery promise\n */\n var renderConfirmRemoveContact = function(header, body, footer, user) {\n if (user) {\n return Str.get_string('removecontactconfirm', 'core_message', truncateUsername(user.fullname))\n .then(function(string) {\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT],\n string,\n '',\n true,\n false\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the delete selected messages dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data If the dialogue should show and checkbox shows to delete message for all users.\n * @return {Object} jQuery promise\n */\n var renderConfirmDeleteSelectedMessages = function(header, body, footer, data) {\n var showmessage = null;\n if (data.type == CONVERSATION_TYPES.SELF) {\n // Message displayed to self-conversations is slighly different.\n showmessage = 'deleteselectedmessagesconfirmselfconversation';\n } else {\n // This other message should be displayed.\n if (data.canDeleteMessagesForAllUsers) {\n showCheckDeleteDialogue(body);\n showmessage = 'deleteforeveryoneselectedmessagesconfirm';\n } else {\n showmessage = 'deleteselectedmessagesconfirm';\n }\n }\n\n if (data.show) {\n return Str.get_string(showmessage, 'core_message')\n .then(function(string) {\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES],\n string,\n '',\n true,\n false\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the confirm delete conversation dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {int|Null} type The conversation type to be removed.\n * @return {Object} jQuery promise\n */\n var renderConfirmDeleteConversation = function(header, body, footer, type) {\n var showmessage = null;\n if (type == CONVERSATION_TYPES.SELF) {\n // Message displayed to self-conversations is slighly different.\n showmessage = 'deleteallselfconfirm';\n } else if (type) {\n // This other message should be displayed.\n showmessage = 'deleteallconfirm';\n }\n\n if (showmessage) {\n return Str.get_string(showmessage, 'core_message')\n .then(function(string) {\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION],\n string,\n '',\n true,\n false\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the confirm delete conversation dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user The other user object.\n * @return {Object} jQuery promise\n */\n var renderConfirmContactRequest = function(header, body, footer, user) {\n if (user) {\n return Str.get_string('userwouldliketocontactyou', 'core_message', truncateUsername(user.fullname))\n .then(function(string) {\n var buttonSelectors = [\n SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST,\n SELECTORS.ACTION_DECLINE_CONTACT_REQUEST\n ];\n return showConfirmDialogue(header, body, footer, buttonSelectors, string, '', false, true);\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Show the checkbox to allow delete message for all.\n *\n * @param {Object} body The body container element.\n */\n var showCheckDeleteDialogue = function(body) {\n var dialogue = getConfirmDialogueContainer(body);\n var checkboxRegion = dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER);\n checkboxRegion.removeClass('hidden');\n };\n\n /**\n * Hide the checkbox to allow delete message for all.\n *\n * @param {Object} body The body container element.\n */\n var hideCheckDeleteDialogue = function(body) {\n var dialogue = getConfirmDialogueContainer(body);\n var checkboxRegion = dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER);\n var checkbox = dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE);\n checkbox.prop('checked', false);\n checkboxRegion.addClass('hidden');\n };\n\n /**\n * Show or hide the block / unblock option in the header dropdown menu.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isBlocked is user blocked.\n */\n var renderIsBlocked = function(header, body, footer, isBlocked) {\n if (isBlocked) {\n header.find(SELECTORS.ACTION_REQUEST_BLOCK).addClass('hidden');\n header.find(SELECTORS.ACTION_REQUEST_UNBLOCK).removeClass('hidden');\n } else {\n header.find(SELECTORS.ACTION_REQUEST_BLOCK).removeClass('hidden');\n header.find(SELECTORS.ACTION_REQUEST_UNBLOCK).addClass('hidden');\n }\n };\n\n /**\n * Show or hide the favourite / unfavourite option in the header dropdown menu\n * and the favourite star in the header title.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} state is this conversation a favourite.\n */\n var renderIsFavourite = function(header, body, footer, state) {\n var favouriteIcon = header.find(SELECTORS.FAVOURITE_ICON_CONTAINER);\n var addFavourite = header.find(SELECTORS.ACTION_CONFIRM_FAVOURITE);\n var removeFavourite = header.find(SELECTORS.ACTION_CONFIRM_UNFAVOURITE);\n\n switch (state) {\n case 'hide':\n favouriteIcon.addClass('hidden');\n addFavourite.addClass('hidden');\n removeFavourite.addClass('hidden');\n break;\n case 'show-add':\n favouriteIcon.addClass('hidden');\n addFavourite.removeClass('hidden');\n removeFavourite.addClass('hidden');\n break;\n case 'show-remove':\n favouriteIcon.removeClass('hidden');\n addFavourite.addClass('hidden');\n removeFavourite.removeClass('hidden');\n break;\n }\n };\n\n /**\n * Show or hide the mute / unmute option in the header dropdown menu\n * and the muted icon in the header title.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {string} state The state of the conversation as defined by the patcher.\n */\n var renderIsMuted = function(header, body, footer, state) {\n var muteIcon = header.find(SELECTORS.MUTED_ICON_CONTAINER);\n var setMuted = header.find(SELECTORS.ACTION_CONFIRM_MUTE);\n var unsetMuted = header.find(SELECTORS.ACTION_CONFIRM_UNMUTE);\n\n switch (state) {\n case 'hide':\n muteIcon.addClass('hidden');\n setMuted.addClass('hidden');\n unsetMuted.addClass('hidden');\n break;\n case 'show-mute':\n muteIcon.addClass('hidden');\n setMuted.removeClass('hidden');\n unsetMuted.addClass('hidden');\n break;\n case 'show-unmute':\n muteIcon.removeClass('hidden');\n setMuted.addClass('hidden');\n unsetMuted.removeClass('hidden');\n break;\n }\n };\n\n /**\n * Show or hide the add / remove user as contact option in the header dropdown menu.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} state the contact state.\n */\n var renderIsContact = function(header, body, footer, state) {\n var addContact = header.find(SELECTORS.ACTION_REQUEST_ADD_CONTACT);\n var removeContact = header.find(SELECTORS.ACTION_REQUEST_REMOVE_CONTACT);\n\n switch (state) {\n case 'pending-contact':\n addContact.addClass('hidden');\n removeContact.addClass('hidden');\n break;\n case 'contact':\n addContact.addClass('hidden');\n removeContact.removeClass('hidden');\n break;\n case 'non-contact':\n addContact.removeClass('hidden');\n removeContact.addClass('hidden');\n break;\n }\n };\n\n /**\n * Show or hide confirm action from confirm dialogue is loading.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isLoading confirm action is loading.\n */\n var renderLoadingConfirmAction = function(header, body, footer, isLoading) {\n var dialogue = getConfirmDialogueContainer(body);\n var buttons = dialogue.find('button');\n var buttonText = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_BUTTON_TEXT);\n var loadingIcon = dialogue.find(SELECTORS.LOADING_ICON_CONTAINER);\n\n if (isLoading) {\n buttons.prop('disabled', true);\n buttonText.addClass('hidden');\n loadingIcon.removeClass('hidden');\n } else {\n buttons.prop('disabled', false);\n buttonText.removeClass('hidden');\n loadingIcon.addClass('hidden');\n }\n };\n\n /**\n * Show or hide the header and footer content for edit mode.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} inEditMode In edit mode or not.\n */\n var renderInEditMode = function(header, body, footer, inEditMode) {\n var messages = null;\n\n if (inEditMode) {\n messages = body.find(SELECTORS.MESSAGE_NOT_SELECTED);\n messages.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).removeClass('hidden');\n hideHeaderContent(header);\n showHeaderEditMode(header);\n } else {\n messages = getMessagesContainer(body);\n messages.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).addClass('hidden');\n messages.find(SELECTORS.MESSAGE_SELECTED_ICON).addClass('hidden');\n showHeaderContent(header);\n hideHeaderEditMode(header);\n }\n };\n\n /**\n * Select or unselect messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data The messages to select or unselect.\n */\n var renderSelectedMessages = function(header, body, footer, data) {\n var hasSelectedMessages = data.count > 0;\n\n if (data.add.length) {\n data.add.forEach(function(messageId) {\n var message = getMessageElement(body, messageId);\n message.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).addClass('hidden');\n message.find(SELECTORS.MESSAGE_SELECTED_ICON).removeClass('hidden');\n message.attr('aria-checked', true);\n });\n }\n\n if (data.remove.length) {\n data.remove.forEach(function(messageId) {\n var message = getMessageElement(body, messageId);\n\n if (hasSelectedMessages) {\n message.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).removeClass('hidden');\n }\n\n message.find(SELECTORS.MESSAGE_SELECTED_ICON).addClass('hidden');\n message.attr('aria-checked', false);\n });\n }\n\n setMessagesSelectedCount(header, data.count);\n };\n\n /**\n * Show or hide the require add contact panel.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data Whether the user has to be added a a contact.\n * @return {Object} jQuery promise\n */\n var renderRequireAddContact = function(header, body, footer, data) {\n if (data.show && !data.hasMessages) {\n return Str.get_strings([\n {\n key: 'requirecontacttomessage',\n component: 'core_message',\n param: data.user.fullname\n },\n {\n key: 'isnotinyourcontacts',\n component: 'core_message',\n param: data.user.fullname\n }\n ])\n .then(function(strings) {\n var title = strings[1];\n var text = strings[0];\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_REQUEST_ADD_CONTACT],\n text,\n title,\n false,\n true\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Show or hide the self-conversation message.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} displayMessage should the message be displayed?.\n * @return {Object|true} jQuery promise\n */\n var renderSelfConversationMessage = function(header, body, footer, displayMessage) {\n var container = getSelfConversationMessageContainer(body);\n if (displayMessage) {\n container.removeClass('hidden');\n } else {\n container.addClass('hidden');\n }\n return true;\n };\n\n /**\n * Show or hide the require add contact panel.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {String} userFullName Full name of the other user.\n * @return {Object|true} jQuery promise\n */\n var renderContactRequestSent = function(header, body, footer, userFullName) {\n var container = getContactRequestSentContainer(body);\n if (userFullName) {\n return Str.get_string('yourcontactrequestpending', 'core_message', truncateUsername(userFullName))\n .then(function(string) {\n container.find(SELECTORS.TEXT).text(string);\n container.removeClass('hidden');\n return string;\n });\n } else {\n container.addClass('hidden');\n return true;\n }\n };\n\n /**\n * Reset the UI to the initial state.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @return {Bool}\n */\n var renderReset = function(header, body, footer) {\n hideConfirmDialogue(header, body, footer);\n hideContactRequestSentContainer(body);\n hideSelfConversationMessageContainer(body);\n hideAllHeaderElements(header);\n showHeaderPlaceholder(header);\n hideAllFooterElements(footer);\n showFooterPlaceholder(footer);\n return true;\n };\n\n var render = function(header, body, footer, patch) {\n var configs = [\n {\n // Resetting the UI needs to come first, if it's required.\n reset: renderReset\n },\n {\n // Any async rendering (stuff that requires templates, strings etc) should\n // go in here.\n conversation: renderConversation,\n header: renderHeader,\n footer: renderFooter,\n confirmBlockUser: renderConfirmBlockUser,\n confirmUnblockUser: renderConfirmUnblockUser,\n confirmAddContact: renderConfirmAddContact,\n confirmRemoveContact: renderConfirmRemoveContact,\n confirmDeleteSelectedMessages: renderConfirmDeleteSelectedMessages,\n confirmDeleteConversation: renderConfirmDeleteConversation,\n confirmContactRequest: renderConfirmContactRequest,\n requireAddContact: renderRequireAddContact,\n selfConversationMessage: renderSelfConversationMessage,\n contactRequestSent: renderContactRequestSent\n },\n {\n loadingMembers: renderLoadingMembers,\n loadingFirstMessages: renderLoadingFirstMessages,\n loadingMessages: renderLoadingMessages,\n isBlocked: renderIsBlocked,\n isContact: renderIsContact,\n isFavourite: renderIsFavourite,\n isMuted: renderIsMuted,\n loadingConfirmAction: renderLoadingConfirmAction,\n inEditMode: renderInEditMode,\n showEmojiPicker: renderShowEmojiPicker,\n showEmojiAutoComplete: renderShowEmojiAutoComplete,\n },\n {\n // Scrolling should be last to make sure everything\n // on the page is visible.\n scrollToMessage: renderScrollToMessage,\n selectedMessages: renderSelectedMessages\n }\n ];\n // Helper function to process each of the configs above.\n var processConfig = function(config) {\n var results = [];\n\n for (var key in patch) {\n if (config.hasOwnProperty(key)) {\n var renderFunc = config[key];\n var patchValue = patch[key];\n results.push(renderFunc(header, body, footer, patchValue));\n }\n }\n\n return results;\n };\n\n // The first config is special because it resets the UI.\n var renderingPromises = processConfig(configs[0]);\n // The second config is special because it contains async rendering.\n renderingPromises = renderingPromises.concat(processConfig(configs[1]));\n\n // Wait for the async rendering to complete before processing the\n // rest of the configs, in order.\n return $.when.apply($, renderingPromises)\n .then(function() {\n for (var i = 2; i < configs.length; i++) {\n processConfig(configs[i]);\n }\n\n return;\n })\n .catch(Notification.exception);\n };\n\n /**\n * Truncate long usernames.\n *\n * @param {String} username Text to truncate.\n * @return {String} Truncated string.\n */\n var truncateUsername = function(username) {\n return Truncate.truncate(username, {\n length: 30,\n words: true,\n ellipsis: '...'\n });\n };\n\n return {\n render: render,\n };\n});\n"],"names":["define","$","Notification","Str","Templates","UserDate","Constants","Aria","Truncate","SELECTORS","TEMPLATES","CONVERSATION_TYPES","getMessagesContainer","body","find","CONTENT_MESSAGES_CONTAINER","getSelfConversationMessageContainer","SELF_CONVERSATION_MESSAGE_CONTAINER","getContactRequestSentContainer","CONTACT_REQUEST_SENT_MESSAGE_CONTAINER","getFooterContentContainer","footer","CONTENT_MESSAGES_FOOTER_CONTAINER","getFooterEditModeContainer","CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER","getFooterPlaceholderContainer","PLACEHOLDER_CONTAINER","showFooterPlaceholder","removeClass","getFooterRequireContactContainer","CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER","getFooterRequireUnblockContainer","CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER","getFooterUnableToMessageContainer","CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER","hideAllFooterElements","addClass","hideFooterContent","hideFooterEditMode","hideFooterPlaceholder","hideFooterRequireContact","hideFooterRequireUnblock","hideFooterUnableToMessage","getContentPlaceholderContainer","CONTENT_PLACEHOLDER_CONTAINER","getHeaderContent","header","HEADER","showHeaderContent","hideHeaderContent","getHeaderEditMode","HEADER_EDIT_MODE","hideHeaderEditMode","getHeaderPlaceholderContainer","HEADER_PLACEHOLDER_CONTAINER","showHeaderPlaceholder","hideHeaderPlaceholder","getMessageElement","messageId","getDayElement","dayTimeCreated","getMoreMessagesLoadingIconContainer","MORE_MESSAGES_LOADING_ICON_CONTAINER","getConfirmDialogueContainer","root","CONFIRM_DIALOGUE_CONTAINER","showConfirmDialogueContainer","container","siblings","hide","get","attr","hideConfirmDialogueContainer","unhide","removeAttr","formatMessagesForTemplate","messages","datesCache","map","message","id","isread","isRead","fromloggedinuser","fromLoggedInUser","userfrom","userFrom","text","formattedtime","timeCreated","renderConversation","data","renderingPromises","hasAddDays","days","add","length","hasAddMessages","hasUpdateMessages","update","timestampsToFormat","datesCachePromise","Deferred","resolve","promise","concat","reduce","carry","day","value","timestamps","push","before","after","get_string","then","format","requests","timestamp","formattedTimes","index","messagesContainer","daysRenderPromises","timestampDate","Date","render","DAY","currentyear","getFullYear","when","apply","forEach","html","element","insertBefore","append","catch","renderAddDays","messagesData","formattedMessages","MESSAGES","messageList","messageHtml","DAY_MESSAGES_CONTAINER","renderAddMessages","formattedTime","LOADING_ICON_CONTAINER","TIME_CREATED","sendState","loading","time","retry","RETRY_SEND","TEXT_CONTAINER","errorMessage","messageContainer","ERROR_MESSAGE_CONTAINER","ERROR_MESSAGE","renderUpdateMessages","remove","renderRemoveDays","renderRemoveMessages","renderHeader","headerContainer","template","HEADER_PUBLIC","context","showrouteback","type","PRIVATE","showControls","HEADER_PRIVATE","HEADER_PRIVATE_NO_CONTROLS","SELF","HEADER_SELF","js","replaceNodeContents","renderFooter","get_strings","key","component","param","user","fullname","strings","title","footerContainer","TITLE","TEXT","showFooterRequireContact","showFooterEditMode","showFooterContent","showFooterRequireUnblock","showFooterUnableToMessage","renderScrollToMessage","position","scrollTop","top","renderLoadingMembers","isLoadingMembers","renderLoadingFirstMessages","isLoadingFirstMessages","hideMessagesContainer","showContentPlaceholder","showMessagesContainer","hideContentPlaceholder","renderLoadingMessages","isLoading","showMoreMessagesLoadingIcon","hideMoreMessagesLoadingIcon","renderShowEmojiPicker","show","EMOJI_PICKER_CONTAINER","getEmojiPickerContainer","EMOJI_PICKER_SEARCH_INPUT","focus","renderShowEmojiAutoComplete","EMOJI_AUTO_COMPLETE_CONTAINER","getEmojiAutoCompleteContainer","showConfirmDialogue","buttonSelectors","bodyText","headerText","canCancel","skipHeader","showOk","dialogue","buttons","selector","cancelButton","CONFIRM_DIALOGUE_CANCEL_BUTTON","okayButton","CONFIRM_DIALOGUE_OKAY_BUTTON","CONFIRM_DIALOGUE_TEXT","dialogueHeader","CONFIRM_DIALOGUE_HEADER","CONFIRM_DIALOGUE","prepend","button","CAN_RECEIVE_FOCUS","filter","first","hideConfirmDialogue","hideCheckDeleteDialogue","renderConfirmBlockUser","username","truncateUsername","canmessageevenifblocked","string","ACTION_CONFIRM_BLOCK","renderConfirmUnblockUser","ACTION_CONFIRM_UNBLOCK","renderConfirmAddContact","userFullName","truncate","words","ellipsis","ACTION_CONFIRM_ADD_CONTACT","renderConfirmRemoveContact","ACTION_CONFIRM_REMOVE_CONTACT","renderConfirmDeleteSelectedMessages","showmessage","canDeleteMessagesForAllUsers","showCheckDeleteDialogue","ACTION_CONFIRM_DELETE_SELECTED_MESSAGES","renderConfirmDeleteConversation","ACTION_CONFIRM_DELETE_CONVERSATION","renderConfirmContactRequest","ACTION_ACCEPT_CONTACT_REQUEST","ACTION_DECLINE_CONTACT_REQUEST","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER","checkboxRegion","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE","prop","renderIsBlocked","isBlocked","ACTION_REQUEST_BLOCK","ACTION_REQUEST_UNBLOCK","renderIsFavourite","state","favouriteIcon","FAVOURITE_ICON_CONTAINER","addFavourite","ACTION_CONFIRM_FAVOURITE","removeFavourite","ACTION_CONFIRM_UNFAVOURITE","renderIsMuted","muteIcon","MUTED_ICON_CONTAINER","setMuted","ACTION_CONFIRM_MUTE","unsetMuted","ACTION_CONFIRM_UNMUTE","renderIsContact","addContact","ACTION_REQUEST_ADD_CONTACT","removeContact","ACTION_REQUEST_REMOVE_CONTACT","renderLoadingConfirmAction","buttonText","CONFIRM_DIALOGUE_BUTTON_TEXT","loadingIcon","renderInEditMode","inEditMode","MESSAGE_NOT_SELECTED","MESSAGE_NOT_SELECTED_ICON","showHeaderEditMode","MESSAGE_SELECTED_ICON","renderSelectedMessages","hasSelectedMessages","count","MESSAGES_SELECTED_COUNT","setMessagesSelectedCount","renderRequireAddContact","hasMessages","renderSelfConversationMessage","displayMessage","renderContactRequestSent","renderReset","hideContactRequestSentContainer","hideSelfConversationMessageContainer","hideAllHeaderElements","patch","configs","reset","conversation","confirmBlockUser","confirmUnblockUser","confirmAddContact","confirmRemoveContact","confirmDeleteSelectedMessages","confirmDeleteConversation","confirmContactRequest","requireAddContact","selfConversationMessage","contactRequestSent","loadingMembers","loadingFirstMessages","loadingMessages","isContact","isFavourite","isMuted","loadingConfirmAction","showEmojiPicker","showEmojiAutoComplete","scrollToMessage","selectedMessages","processConfig","config","results","hasOwnProperty","renderFunc","patchValue","i","exception"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"message_drawer_view_conversation_renderer.min.js","sources":["../src/message_drawer_view_conversation_renderer.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 .\n\n/**\n * This module updates the UI for the conversation page in the message\n * drawer.\n *\n * The module will take a patch from the message_drawer_view_conversation_patcher\n * module and update the UI to reflect the changes.\n *\n * This is the only module that ever modifies the UI of the conversation page.\n *\n * @module core_message/message_drawer_view_conversation_renderer\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/str',\n 'core/templates',\n 'core/user_date',\n 'core_message/message_drawer_view_conversation_constants',\n 'core/aria',\n 'core/truncate',\n],\nfunction(\n $,\n Notification,\n Str,\n Templates,\n UserDate,\n Constants,\n Aria,\n Truncate,\n) {\n var SELECTORS = Constants.SELECTORS;\n var TEMPLATES = Constants.TEMPLATES;\n var CONVERSATION_TYPES = Constants.CONVERSATION_TYPES;\n\n /**\n * Get the messages container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var getMessagesContainer = function(body) {\n return body.find(SELECTORS.CONTENT_MESSAGES_CONTAINER);\n };\n\n /**\n * Show the messages container element.\n *\n * @param {Object} body Conversation body container element.\n */\n var showMessagesContainer = function(body) {\n getMessagesContainer(body).removeClass('hidden');\n };\n\n /**\n * Hide the messages container element.\n *\n * @param {Object} body Conversation body container element.\n */\n var hideMessagesContainer = function(body) {\n getMessagesContainer(body).addClass('hidden');\n };\n\n /**\n * Get the self-conversation message container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var getSelfConversationMessageContainer = function(body) {\n return body.find(SELECTORS.SELF_CONVERSATION_MESSAGE_CONTAINER);\n };\n\n /**\n * Get the unable to message container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The unable to message container element.\n */\n const getUnableToMessageContainer = (body) => {\n return body.find(SELECTORS.UNABLE_TO_MESSAGE_CONTAINER);\n };\n\n /**\n * Get the message text area element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The message text area element.\n */\n const getMessageTextArea = (footer) => {\n return footer.find(SELECTORS.MESSAGE_TEXT_AREA);\n };\n\n /**\n * Get the send message button element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The send message button element.\n */\n const getSendMessageButton = (footer) => {\n return footer.find(SELECTORS.SEND_MESSAGE_BUTTON);\n };\n\n /**\n * Get the emoji picker button element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The emoji picker button element.\n */\n const getEmojiPickerButton = (footer) => {\n return footer.find(SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON);\n };\n\n /**\n * Hide the self-conversation message container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var hideSelfConversationMessageContainer = function(body) {\n return getSelfConversationMessageContainer(body).addClass('hidden');\n };\n\n /**\n * Get the contact request sent container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var getContactRequestSentContainer = function(body) {\n return body.find(SELECTORS.CONTACT_REQUEST_SENT_MESSAGE_CONTAINER);\n };\n\n /**\n * Hide the contact request sent container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The messages container element.\n */\n var hideContactRequestSentContainer = function(body) {\n return getContactRequestSentContainer(body).addClass('hidden');\n };\n\n /**\n * Get the footer container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer container element.\n */\n var getFooterContentContainer = function(footer) {\n return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_CONTAINER);\n };\n\n /**\n * Show the footer container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterContent = function(footer) {\n getFooterContentContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterContent = function(footer) {\n getFooterContentContainer(footer).addClass('hidden');\n };\n\n /**\n * Get the footer edit mode container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer container element.\n */\n var getFooterEditModeContainer = function(footer) {\n return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER);\n };\n\n /**\n * Show the footer edit mode container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterEditMode = function(footer) {\n getFooterEditModeContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer edit mode container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterEditMode = function(footer) {\n getFooterEditModeContainer(footer).addClass('hidden');\n };\n\n /**\n * Get the footer placeholder.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer placeholder container element.\n */\n var getFooterPlaceholderContainer = function(footer) {\n return footer.find(SELECTORS.PLACEHOLDER_CONTAINER);\n };\n\n /**\n * Show the footer placeholder\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterPlaceholder = function(footer) {\n getFooterPlaceholderContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer placeholder\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterPlaceholder = function(footer) {\n getFooterPlaceholderContainer(footer).addClass('hidden');\n };\n\n /**\n * Get the footer Require add as contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer Require add as contact container element.\n */\n var getFooterRequireContactContainer = function(footer) {\n return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER);\n };\n\n /**\n * Show the footer add as contact dialogue.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterRequireContact = function(footer) {\n getFooterRequireContactContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer add as contact dialogue.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterRequireContact = function(footer) {\n getFooterRequireContactContainer(footer).addClass('hidden');\n };\n\n /**\n * Get the footer Required to unblock contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The footer Required to unblock contact container element.\n */\n var getFooterRequireUnblockContainer = function(footer) {\n return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER);\n };\n\n /**\n * Show the footer Required to unblock contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var showFooterRequireUnblock = function(footer) {\n getFooterRequireUnblockContainer(footer).removeClass('hidden');\n };\n\n /**\n * Hide the footer Required to unblock contact container element.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideFooterRequireUnblock = function(footer) {\n getFooterRequireUnblockContainer(footer).addClass('hidden');\n };\n\n\n /**\n * Hide all header elements.\n *\n * @param {Object} header Conversation header container element.\n */\n var hideAllHeaderElements = function(header) {\n hideHeaderContent(header);\n hideHeaderEditMode(header);\n hideHeaderPlaceholder(header);\n };\n\n /**\n * Hide all footer dialogues and messages.\n *\n * @param {Object} footer Conversation footer container element.\n */\n var hideAllFooterElements = function(footer) {\n hideFooterContent(footer);\n hideFooterEditMode(footer);\n hideFooterPlaceholder(footer);\n hideFooterRequireContact(footer);\n hideFooterRequireUnblock(footer);\n };\n\n /**\n * Get the content placeholder container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The body placeholder container element.\n */\n var getContentPlaceholderContainer = function(body) {\n return body.find(SELECTORS.CONTENT_PLACEHOLDER_CONTAINER);\n };\n\n /**\n * Show the content placeholder.\n *\n * @param {Object} body Conversation body container element.\n */\n var showContentPlaceholder = function(body) {\n getContentPlaceholderContainer(body).removeClass('hidden');\n };\n\n /**\n * Hide the content placeholder.\n *\n * @param {Object} body Conversation body container element.\n */\n var hideContentPlaceholder = function(body) {\n getContentPlaceholderContainer(body).addClass('hidden');\n };\n\n /**\n * Get the header content container element.\n *\n * @param {Object} header Conversation header container element.\n * @return {Object} The header content container element.\n */\n var getHeaderContent = function(header) {\n return header.find(SELECTORS.HEADER);\n };\n\n /**\n * Show the header content.\n *\n * @param {Object} header Conversation header container element.\n */\n var showHeaderContent = function(header) {\n getHeaderContent(header).removeClass('hidden');\n };\n\n /**\n * Hide the header content.\n *\n * @param {Object} header Conversation header container element.\n */\n var hideHeaderContent = function(header) {\n getHeaderContent(header).addClass('hidden');\n };\n\n /**\n * Show the unable to message container.\n *\n * @param {Object} body Conversation body container element.\n */\n const showUnableToMessage = (body) => {\n getUnableToMessageContainer(body).removeClass('hidden');\n };\n\n /**\n * Hide the unable to message container.\n *\n * @param {Object} body Conversation body container element.\n */\n const hideUnableToMessage = (body) => {\n getUnableToMessageContainer(body).addClass('hidden');\n };\n\n /**\n * Enable the emoji picker button.\n *\n * @param {Object} footer Conversation footer container element.\n */\n const enableEmojiPickerButton = (footer) => {\n getEmojiPickerButton(footer).prop('disabled', false);\n };\n\n /**\n * Enable the send message button.\n *\n * @param {Object} footer Conversation footer container element.\n */\n const enableSendMessageButton = (footer) => {\n getSendMessageButton(footer).prop('disabled', false);\n };\n\n /**\n * Enable the message text area.\n *\n * @param {Object} footer Conversation footer container element.\n */\n const enableMessageTextArea = (footer) => {\n const messageTextArea = getMessageTextArea(footer);\n messageTextArea.prop('disabled', false);\n Aria.unhide(messageTextArea.get());\n\n Str.get_string('writeamessage', 'core_message').done(function(string) {\n if (!messageTextArea.prop('disabled')) {\n messageTextArea.prop('placeholder', string);\n }\n });\n };\n\n /**\n * Enable all footer content elements.\n *\n * @param {Object} footer Conversation footer container element.\n */\n const enableAllFooterContentElements = (footer) => {\n enableEmojiPickerButton(footer);\n enableSendMessageButton(footer);\n enableMessageTextArea(footer);\n };\n\n /**\n * Disable the emoji picker button.\n *\n * @param {Object} footer Conversation footer container element.\n */\n const disableEmojiPickerButton = (footer) => {\n getEmojiPickerButton(footer).prop('disabled', true);\n };\n\n /**\n * Disable the send message button.\n *\n * @param {Object} footer Conversation footer container element.\n */\n const disableSendMessageButton = (footer) => {\n getSendMessageButton(footer).prop('disabled', true);\n };\n\n /**\n * Disable the message text area.\n *\n * @param {Object} footer Conversation footer container element.\n */\n const disableMessageTextArea = (footer) => {\n const messageTextArea = getMessageTextArea(footer);\n messageTextArea.prop('disabled', true);\n messageTextArea.removeAttr('placeholder');\n Aria.unhide(messageTextArea.get());\n };\n\n /**\n * Disable all footer content elements.\n *\n * @param {Object} footer Conversation footer container element.\n */\n const disableAllFooterContentElements = (footer) => {\n disableEmojiPickerButton(footer);\n disableSendMessageButton(footer);\n disableMessageTextArea(footer);\n };\n\n /**\n * Get the header edit mode container element.\n *\n * @param {Object} header Conversation header container element.\n * @return {Object} The header content container element.\n */\n var getHeaderEditMode = function(header) {\n return header.find(SELECTORS.HEADER_EDIT_MODE);\n };\n\n /**\n * Show the header edit mode container.\n *\n * @param {Object} header Conversation header container element.\n */\n var showHeaderEditMode = function(header) {\n getHeaderEditMode(header).removeClass('hidden');\n };\n\n /**\n * Hide the header edit mode container.\n *\n * @param {Object} header Conversation header container element.\n */\n var hideHeaderEditMode = function(header) {\n getHeaderEditMode(header).addClass('hidden');\n };\n\n /**\n * Get the header placeholder container element.\n *\n * @param {Object} header Conversation header container element.\n * @return {Object} The header placeholder container element.\n */\n var getHeaderPlaceholderContainer = function(header) {\n return header.find(SELECTORS.HEADER_PLACEHOLDER_CONTAINER);\n };\n\n /**\n * Show the header placeholder.\n *\n * @param {Object} header Conversation header container element.\n */\n var showHeaderPlaceholder = function(header) {\n getHeaderPlaceholderContainer(header).removeClass('hidden');\n };\n\n /**\n * Hide the header placeholder.\n *\n * @param {Object} header Conversation header container element.\n */\n var hideHeaderPlaceholder = function(header) {\n getHeaderPlaceholderContainer(header).addClass('hidden');\n };\n\n /**\n * Get the emoji picker container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The emoji picker container element.\n */\n var getEmojiPickerContainer = function(footer) {\n return footer.find(SELECTORS.EMOJI_PICKER_CONTAINER);\n };\n\n /**\n * Get the emoji picker container element.\n *\n * @param {Object} footer Conversation footer container element.\n * @return {Object} The emoji picker container element.\n */\n var getEmojiAutoCompleteContainer = function(footer) {\n return footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER);\n };\n\n /**\n * Get a message element.\n *\n * @param {Object} body Conversation body container element.\n * @param {Number} messageId the Message id.\n * @return {Object} A message element from the conversation.\n */\n var getMessageElement = function(body, messageId) {\n var messagesContainer = getMessagesContainer(body);\n return messagesContainer.find('[data-message-id=\"' + messageId + '\"]');\n };\n\n /**\n * Get the day container element. The day container element holds a list of messages for that day.\n *\n * @param {Object} body Conversation body container element.\n * @param {Number} dayTimeCreated Midnight timestamp for the day.\n * @return {Object} jQuery object\n */\n var getDayElement = function(body, dayTimeCreated) {\n var messagesContainer = getMessagesContainer(body);\n return messagesContainer.find('[data-day-id=\"' + dayTimeCreated + '\"]');\n };\n\n /**\n * Get the more messages loading icon container element.\n *\n * @param {Object} body Conversation body container element.\n * @return {Object} The more messages loading container element.\n */\n var getMoreMessagesLoadingIconContainer = function(body) {\n return body.find(SELECTORS.MORE_MESSAGES_LOADING_ICON_CONTAINER);\n };\n\n /**\n * Show the more messages loading icon.\n *\n * @param {Object} body Conversation body container element.\n */\n var showMoreMessagesLoadingIcon = function(body) {\n getMoreMessagesLoadingIconContainer(body).removeClass('hidden');\n };\n\n /**\n * Hide the more messages loading icon.\n *\n * @param {Object} body Conversation body container element.\n */\n var hideMoreMessagesLoadingIcon = function(body) {\n getMoreMessagesLoadingIconContainer(body).addClass('hidden');\n };\n\n /**\n * Get the confirm dialogue container element.\n *\n * @param {Object} root The container element to search.\n * @return {Object} The confirm dialogue container element.\n */\n var getConfirmDialogueContainer = function(root) {\n return root.find(SELECTORS.CONFIRM_DIALOGUE_CONTAINER);\n };\n\n /**\n * Show the confirm dialogue container element.\n *\n * @param {Object} root The container element containing a dialogue.\n */\n var showConfirmDialogueContainer = function(root) {\n var container = getConfirmDialogueContainer(root);\n var siblings = container.siblings(':not(.hidden)');\n Aria.hide(siblings.get());\n siblings.attr('data-confirm-dialogue-hidden', true);\n\n container.removeClass('hidden');\n };\n\n /**\n * Hide the confirm dialogue container element.\n *\n * @param {Object} root The container element containing a dialogue.\n */\n var hideConfirmDialogueContainer = function(root) {\n var container = getConfirmDialogueContainer(root);\n var siblings = container.siblings('[data-confirm-dialogue-hidden=\"true\"]');\n Aria.unhide(siblings.get());\n siblings.removeAttr('data-confirm-dialogue-hidden');\n\n container.addClass('hidden');\n };\n\n /**\n * Set the number of selected messages.\n *\n * @param {Object} header The header container element.\n * @param {Number} value The new number to display.\n */\n var setMessagesSelectedCount = function(header, value) {\n getHeaderEditMode(header).find(SELECTORS.MESSAGES_SELECTED_COUNT).text(value);\n };\n\n /**\n * Format message for the mustache template, transform camelCase properties to lowercase properties.\n *\n * @param {Array} messages Array of message objects.\n * @param {Object} datesCache Cache timestamps and their formatted date string.\n * @return {Array} Messages formated for mustache template.\n */\n var formatMessagesForTemplate = function(messages, datesCache) {\n return messages.map(function(message) {\n return {\n id: message.id,\n isread: message.isRead,\n fromloggedinuser: message.fromLoggedInUser,\n userfrom: message.userFrom,\n text: message.text,\n formattedtime: message.timeCreated ? datesCache[message.timeCreated] : null\n };\n });\n };\n\n /**\n * Create rendering promises for each day containing messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Array} days Array of days containing messages.\n * @param {Object} datesCache Cache timestamps and their formatted date string.\n * @param {Boolean} isAbleToMessage Whether the user can send a message to the conversation.\n * @return {Promise} Days rendering promises.\n */\n var renderAddDays = function(header, body, footer, days, datesCache, isAbleToMessage) {\n var messagesContainer = getMessagesContainer(body);\n var daysRenderPromises = days.map(function(data) {\n var timestampDate = new Date(data.value.timestamp * 1000);\n return Templates.render(TEMPLATES.DAY, {\n timestamp: data.value.timestamp,\n currentyear: timestampDate.getFullYear() === (new Date()).getFullYear(),\n messages: formatMessagesForTemplate(data.value.messages, datesCache)\n });\n });\n\n return $.when.apply($, daysRenderPromises).then(function() {\n // Wait until all of the rendering is done for each of the days\n // to ensure they are added to the page in the correct order.\n days.forEach(function(data, index) {\n // eslint-disable-next-line promise/no-nesting\n daysRenderPromises[index]\n .then(function(html) {\n if (data.before) {\n var element = getDayElement(body, data.before.timestamp);\n return $(html).insertBefore(element);\n } else {\n return messagesContainer.append(html);\n }\n })\n .always(function() {\n const showUnableToMessage = !isAbleToMessage && index === days.length - 1;\n messagesContainer.find(SELECTORS.DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER)\n .last().toggleClass('hidden', !showUnableToMessage);\n hideUnableToMessage(body);\n })\n .catch(function() {\n // Fail silently.\n });\n });\n\n return;\n });\n };\n\n /**\n * Add (more) messages to day containers.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Array} messages List of messages.\n * @param {Object} datesCache Cache timestamps and their formatted date string.\n * @return {Promise} Messages rendering promises.\n */\n var renderAddMessages = function(header, body, footer, messages, datesCache) {\n var messagesData = messages.map(function(data) {\n return data.value;\n });\n var formattedMessages = formatMessagesForTemplate(messagesData, datesCache);\n\n return Templates.render(TEMPLATES.MESSAGES, {messages: formattedMessages})\n .then(function(html) {\n var messageList = $(html);\n messages.forEach(function(data) {\n var messageHtml = messageList.find('[data-message-id=\"' + data.value.id + '\"]');\n if (data.before) {\n var element = getMessageElement(body, data.before.id);\n return messageHtml.insertBefore(element);\n } else {\n var dayContainer = getDayElement(body, data.day.timestamp);\n var dayMessagesContainer = dayContainer.find(SELECTORS.DAY_MESSAGES_CONTAINER);\n return dayMessagesContainer.append(messageHtml);\n }\n });\n\n return;\n });\n };\n\n /**\n * Update existing messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Array} messages List of messages.\n * @param {Object} datesCache Cache timestamps and their formatted date string.\n */\n var renderUpdateMessages = function(header, body, footer, messages, datesCache) {\n messages.forEach(function(message) {\n var before = message.before;\n var after = message.after;\n var element = getMessageElement(body, before.id);\n\n if (before.id != after.id) {\n element.attr('data-message-id', after.id);\n }\n\n if (before.timeCreated != after.timeCreated) {\n var formattedTime = datesCache[after.timeCreated];\n element.find(SELECTORS.LOADING_ICON_CONTAINER).addClass('hidden');\n element.find(SELECTORS.TIME_CREATED).text(formattedTime).removeClass('hidden');\n }\n\n if (before.sendState != after.sendState) {\n var loading = element.find(SELECTORS.LOADING_ICON_CONTAINER);\n var time = element.find(SELECTORS.TIME_CREATED);\n var retry = element.find(SELECTORS.RETRY_SEND);\n\n loading.addClass('hidden');\n Aria.hide(loading.get());\n\n time.addClass('hidden');\n Aria.hide(time.get());\n\n retry.addClass('hidden');\n Aria.hide(retry.get());\n\n element.removeClass('border border-danger');\n\n switch (after.sendState) {\n case 'pending':\n loading.removeClass('hidden');\n Aria.unhide(loading.get());\n break;\n case 'error':\n retry.removeClass('hidden');\n Aria.unhide(retry.get());\n element.addClass('border border-danger');\n break;\n case 'sent':\n time.removeClass('hidden');\n Aria.unhide(time.get());\n break;\n }\n }\n\n if (before.text != after.text) {\n element.find(SELECTORS.TEXT_CONTAINER).html(after.text);\n }\n\n if (before.errorMessage != after.errorMessage) {\n var messageContainer = element.find(SELECTORS.ERROR_MESSAGE_CONTAINER);\n var message = messageContainer.find(SELECTORS.ERROR_MESSAGE);\n\n if (after.errorMessage) {\n messageContainer.removeClass('hidden');\n Aria.unhide(messageContainer.get());\n message.text(after.errorMessage);\n } else {\n messageContainer.addClass('hidden');\n Aria.unhide(messageContainer.get());\n message.text('');\n }\n }\n });\n };\n\n /**\n * Remove days from conversation.\n *\n * @param {Object} body The body container element.\n * @param {Array} days Array of days to be removed.\n */\n var renderRemoveDays = function(body, days) {\n days.forEach(function(data) {\n getDayElement(body, data.timestamp).remove();\n });\n };\n\n /**\n * Remove messages from conversation.\n *\n * @param {Object} body The body container element.\n * @param {Array} messages Array of messages to be removed.\n */\n var renderRemoveMessages = function(body, messages) {\n messages.forEach(function(data) {\n getMessageElement(body, data.id).remove();\n });\n };\n\n /**\n * Render the full conversation base on input from the statemanager.\n *\n * This will pre-load all of the formatted timestamps for each message that\n * needs to render to reduce the number of networks requests.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data The conversation diff.\n * @return {Object} jQuery promise.\n */\n var renderConversation = function(header, body, footer, data) {\n var renderingPromises = [];\n var hasAddDays = data.days.add.length > 0;\n var hasAddMessages = data.messages.add.length > 0;\n var hasUpdateMessages = data.messages.update.length > 0;\n var timestampsToFormat = [];\n var datesCachePromise = $.Deferred().resolve({}).promise();\n\n if (hasAddDays) {\n // Search for all of the timeCreated values in all of the messages in all of\n // the days that we need to render.\n timestampsToFormat = timestampsToFormat.concat(data.days.add.reduce(function(carry, day) {\n return carry.concat(day.value.messages.reduce(function(timestamps, message) {\n if (message.timeCreated) {\n timestamps.push(message.timeCreated);\n }\n return timestamps;\n }, []));\n }, []));\n }\n\n if (hasAddMessages) {\n // Search for all of the timeCreated values in all of the messages that we\n // need to render.\n timestampsToFormat = timestampsToFormat.concat(data.messages.add.reduce(function(timestamps, message) {\n if (message.value.timeCreated) {\n timestamps.push(message.value.timeCreated);\n }\n return timestamps;\n }, []));\n }\n\n if (hasUpdateMessages) {\n timestampsToFormat = timestampsToFormat.concat(data.messages.update.reduce(function(timestamps, message) {\n if (message.before.timeCreated != message.after.timeCreated) {\n timestamps.push(message.after.timeCreated);\n }\n return timestamps;\n }, []));\n }\n\n if (timestampsToFormat.length) {\n // If we have timestamps then pre-load the formatted version of each of them\n // in a single request to the server. This saves the templates doing multiple\n // individual requests.\n datesCachePromise = Str.get_string('strftimetime24', 'core_langconfig')\n .then(function(format) {\n var requests = timestampsToFormat.map(function(timestamp) {\n return {\n timestamp: timestamp,\n format: format\n };\n });\n\n return UserDate.get(requests);\n })\n .then(function(formattedTimes) {\n return timestampsToFormat.reduce(function(carry, timestamp, index) {\n carry[timestamp] = formattedTimes[index];\n return carry;\n }, {});\n });\n }\n\n if (hasAddDays) {\n renderingPromises.push(datesCachePromise.then(function(datesCache) {\n return renderAddDays(header, body, footer, data.days.add, datesCache, !data.unableToMessage);\n }));\n }\n\n if (hasAddMessages) {\n renderingPromises.push(datesCachePromise.then(function(datesCache) {\n return renderAddMessages(header, body, footer, data.messages.add, datesCache);\n }));\n }\n\n if (hasUpdateMessages) {\n renderingPromises.push(datesCachePromise.then(function(datesCache) {\n return renderUpdateMessages(header, body, footer, data.messages.update, datesCache);\n }));\n }\n\n if (data.days.remove.length > 0) {\n renderRemoveDays(body, data.days.remove);\n }\n\n if (data.messages.remove.length > 0) {\n renderRemoveMessages(body, data.messages.remove);\n }\n\n return $.when.apply($, renderingPromises);\n };\n\n /**\n * Render the conversation header.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data Data for header.\n * @return {Object} jQuery promise\n */\n var renderHeader = function(header, body, footer, data) {\n var headerContainer = getHeaderContent(header);\n var template = TEMPLATES.HEADER_PUBLIC;\n data.context.showrouteback = (header.attr('data-from-panel') === \"false\");\n if (data.type == CONVERSATION_TYPES.PRIVATE) {\n template = data.showControls ? TEMPLATES.HEADER_PRIVATE : TEMPLATES.HEADER_PRIVATE_NO_CONTROLS;\n } else if (data.type == CONVERSATION_TYPES.SELF) {\n template = TEMPLATES.HEADER_SELF;\n }\n\n return Templates.render(template, data.context)\n .then(function(html, js) {\n Templates.replaceNodeContents(headerContainer, html, js);\n return;\n });\n };\n\n /**\n * Render the conversation footer.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data Data for footer.\n * @return {Object} jQuery promise.\n */\n var renderFooter = function(header, body, footer, data) {\n hideAllFooterElements(footer);\n\n switch (data.type) {\n case 'placeholder':\n return showFooterPlaceholder(footer);\n case 'add-contact':\n return Str.get_strings([\n {\n key: 'requirecontacttomessage',\n component: 'core_message',\n param: data.user.fullname\n },\n {\n key: 'isnotinyourcontacts',\n component: 'core_message',\n param: data.user.fullname\n }\n ])\n .then(function(strings) {\n var title = strings[1];\n var text = strings[0];\n var footerContainer = getFooterRequireContactContainer(footer);\n footerContainer.find(SELECTORS.TITLE).text(title);\n footerContainer.find(SELECTORS.TEXT).text(text);\n showFooterRequireContact(footer);\n return strings;\n });\n case 'edit-mode':\n return showFooterEditMode(footer);\n case 'content':\n return showFooterContent(footer);\n case 'unblock':\n return showFooterRequireUnblock(footer);\n }\n\n return true;\n };\n\n /**\n * Scroll to a message in the conversation.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Number} messageId Message id.\n */\n var renderScrollToMessage = function(header, body, footer, messageId) {\n var messagesContainer = getMessagesContainer(body);\n var messageElement = getMessageElement(body, messageId);\n var position = messageElement.position();\n // Scroll the message container down to the top of the message element.\n if (position) {\n var scrollTop = messagesContainer.scrollTop() + position.top;\n messagesContainer.scrollTop(scrollTop);\n }\n };\n\n /**\n * Hide or show the conversation header.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isLoadingMembers Members loading.\n */\n var renderLoadingMembers = function(header, body, footer, isLoadingMembers) {\n if (isLoadingMembers) {\n hideHeaderContent(header);\n showHeaderPlaceholder(header);\n } else {\n showHeaderContent(header);\n hideHeaderPlaceholder(header);\n }\n };\n\n /**\n * Hide or show loading conversation messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isLoadingFirstMessages Messages loading.\n */\n var renderLoadingFirstMessages = function(header, body, footer, isLoadingFirstMessages) {\n if (isLoadingFirstMessages) {\n hideMessagesContainer(body);\n showContentPlaceholder(body);\n } else {\n showMessagesContainer(body);\n hideContentPlaceholder(body);\n }\n };\n\n /**\n * Hide or show loading more messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isLoading Messages loading.\n */\n var renderLoadingMessages = function(header, body, footer, isLoading) {\n if (isLoading) {\n showMoreMessagesLoadingIcon(body);\n } else {\n hideMoreMessagesLoadingIcon(body);\n }\n };\n\n /**\n * Hide or show the emoji picker.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} show Should the emoji picker be visible.\n */\n var renderShowEmojiPicker = function(header, body, footer, show) {\n var container = getEmojiPickerContainer(footer);\n\n if (show) {\n container.removeClass('hidden');\n Aria.unhide(container.get());\n container.find(SELECTORS.EMOJI_PICKER_SEARCH_INPUT).focus();\n } else {\n container.addClass('hidden');\n Aria.hide(container.get());\n }\n };\n\n /**\n * Hide or show the emoji auto complete.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} show Should the emoji picker be visible.\n */\n var renderShowEmojiAutoComplete = function(header, body, footer, show) {\n var container = getEmojiAutoCompleteContainer(footer);\n\n if (show) {\n container.removeClass('hidden');\n Aria.unhide(container.get());\n } else {\n container.addClass('hidden');\n Aria.hide(container.get());\n }\n };\n\n /**\n * Show a confirmation dialogue\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {String} buttonSelectors Selectors for the buttons to show.\n * @param {String} bodyText Text to show in dialogue.\n * @param {String} headerText Text to show in dialogue header.\n * @param {Bool} canCancel Can this dialogue be cancelled.\n * @param {Bool} skipHeader Skip blanking out the header\n * @param {Bool} showOk Show an 'Okay' button for a dialogue which will close it\n */\n var showConfirmDialogue = function(\n header,\n body,\n footer,\n buttonSelectors,\n bodyText,\n headerText,\n canCancel,\n skipHeader,\n showOk\n ) {\n var dialogue = getConfirmDialogueContainer(body);\n var buttons = buttonSelectors.map(function(selector) {\n return dialogue.find(selector);\n });\n var cancelButton = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_CANCEL_BUTTON);\n var okayButton = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_OKAY_BUTTON);\n var text = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_TEXT);\n var dialogueHeader = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_HEADER);\n\n dialogue.find('button').addClass('hidden');\n\n if (canCancel) {\n cancelButton.removeClass('hidden');\n } else {\n cancelButton.addClass('hidden');\n }\n\n if (showOk) {\n okayButton.removeClass('hidden');\n } else {\n okayButton.addClass('hidden');\n }\n\n if (headerText) {\n // Create the dialogue header.\n dialogueHeader = $('

    ');\n dialogueHeader.text(headerText);\n // Prepend it to the confirmation body.\n var confirmDialogue = dialogue.find(SELECTORS.CONFIRM_DIALOGUE);\n confirmDialogue.prepend(dialogueHeader);\n } else if (dialogueHeader.length) {\n // Header text is empty but dialogue header is present, so remove it.\n dialogueHeader.remove();\n }\n\n buttons.forEach(function(button) {\n button.removeClass('hidden');\n });\n text.text(bodyText);\n showConfirmDialogueContainer(footer);\n showConfirmDialogueContainer(body);\n\n if (!skipHeader) {\n showConfirmDialogueContainer(header);\n }\n\n dialogue.find(SELECTORS.CAN_RECEIVE_FOCUS).filter(':visible').first().focus();\n };\n\n /**\n * Hide the dialogue\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @return {Bool} always true.\n */\n var hideConfirmDialogue = function(header, body, footer) {\n var dialogue = getConfirmDialogueContainer(body);\n var cancelButton = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_CANCEL_BUTTON);\n var okayButton = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_OKAY_BUTTON);\n var text = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_TEXT);\n var dialogueHeader = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_HEADER);\n\n hideCheckDeleteDialogue(body);\n hideConfirmDialogueContainer(body);\n hideConfirmDialogueContainer(footer);\n hideConfirmDialogueContainer(header);\n dialogue.find('button').addClass('hidden');\n cancelButton.removeClass('hidden');\n okayButton.removeClass('hidden');\n text.text('');\n\n // Remove dialogue header if present.\n if (dialogueHeader.length) {\n dialogueHeader.remove();\n }\n\n header.find(SELECTORS.CAN_RECEIVE_FOCUS).first().focus();\n return true;\n };\n\n /**\n * Render the confirm block user dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user User to block.\n * @return {Object} jQuery promise\n */\n var renderConfirmBlockUser = function(header, body, footer, user) {\n if (user) {\n const username = truncateUsername(user.fullname);\n if (user.canmessageevenifblocked) {\n return Str.get_string('cantblockuser', 'core_message', username)\n .then(function(string) {\n return showConfirmDialogue(header, body, footer, [], string, '', false, false, true);\n });\n } else {\n return Str.get_string('blockuserconfirm', 'core_message', username)\n .then(function(string) {\n return showConfirmDialogue(header, body, footer, [SELECTORS.ACTION_CONFIRM_BLOCK], string, '', true, false);\n });\n }\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the confirm unblock user dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user User to unblock.\n * @return {Object} jQuery promise\n */\n var renderConfirmUnblockUser = function(header, body, footer, user) {\n if (user) {\n return Str.get_string('unblockuserconfirm', 'core_message', truncateUsername(user.fullname))\n .then(function(string) {\n return showConfirmDialogue(header, body, footer, [SELECTORS.ACTION_CONFIRM_UNBLOCK], string, '', true, false);\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the add user as contact dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user User to add as contact.\n * @return {Object} jQuery promise\n */\n var renderConfirmAddContact = function(header, body, footer, user) {\n if (user) {\n // Truncate long usernames.\n const userFullName = Truncate.truncate(user.fullname, {\n length: 30,\n words: true,\n ellipsis: '...'\n });\n return Str.get_string('addcontactconfirm', 'core_message', userFullName)\n .then(function(string) {\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_CONFIRM_ADD_CONTACT],\n string,\n '',\n true,\n false\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the remove user from contacts dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user User to remove from contacts.\n * @return {Object} jQuery promise\n */\n var renderConfirmRemoveContact = function(header, body, footer, user) {\n if (user) {\n return Str.get_string('removecontactconfirm', 'core_message', truncateUsername(user.fullname))\n .then(function(string) {\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT],\n string,\n '',\n true,\n false\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the delete selected messages dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data If the dialogue should show and checkbox shows to delete message for all users.\n * @return {Object} jQuery promise\n */\n var renderConfirmDeleteSelectedMessages = function(header, body, footer, data) {\n var showmessage = null;\n if (data.type == CONVERSATION_TYPES.SELF) {\n // Message displayed to self-conversations is slighly different.\n showmessage = 'deleteselectedmessagesconfirmselfconversation';\n } else {\n // This other message should be displayed.\n if (data.canDeleteMessagesForAllUsers) {\n showCheckDeleteDialogue(body);\n showmessage = 'deleteforeveryoneselectedmessagesconfirm';\n } else {\n showmessage = 'deleteselectedmessagesconfirm';\n }\n }\n\n if (data.show) {\n return Str.get_string(showmessage, 'core_message')\n .then(function(string) {\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES],\n string,\n '',\n true,\n false\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the confirm delete conversation dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {int|Null} type The conversation type to be removed.\n * @return {Object} jQuery promise\n */\n var renderConfirmDeleteConversation = function(header, body, footer, type) {\n var showmessage = null;\n if (type == CONVERSATION_TYPES.SELF) {\n // Message displayed to self-conversations is slighly different.\n showmessage = 'deleteallselfconfirm';\n } else if (type) {\n // This other message should be displayed.\n showmessage = 'deleteallconfirm';\n }\n\n if (showmessage) {\n return Str.get_string(showmessage, 'core_message')\n .then(function(string) {\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION],\n string,\n '',\n true,\n false\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Render the confirm delete conversation dialogue.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} user The other user object.\n * @return {Object} jQuery promise\n */\n var renderConfirmContactRequest = function(header, body, footer, user) {\n if (user) {\n return Str.get_string('userwouldliketocontactyou', 'core_message', truncateUsername(user.fullname))\n .then(function(string) {\n var buttonSelectors = [\n SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST,\n SELECTORS.ACTION_DECLINE_CONTACT_REQUEST\n ];\n return showConfirmDialogue(header, body, footer, buttonSelectors, string, '', false, true);\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Show the checkbox to allow delete message for all.\n *\n * @param {Object} body The body container element.\n */\n var showCheckDeleteDialogue = function(body) {\n var dialogue = getConfirmDialogueContainer(body);\n var checkboxRegion = dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER);\n checkboxRegion.removeClass('hidden');\n };\n\n /**\n * Hide the checkbox to allow delete message for all.\n *\n * @param {Object} body The body container element.\n */\n var hideCheckDeleteDialogue = function(body) {\n var dialogue = getConfirmDialogueContainer(body);\n var checkboxRegion = dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER);\n var checkbox = dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE);\n checkbox.prop('checked', false);\n checkboxRegion.addClass('hidden');\n };\n\n /**\n * Show or hide the block / unblock option in the header dropdown menu.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isBlocked is user blocked.\n */\n var renderIsBlocked = function(header, body, footer, isBlocked) {\n if (isBlocked) {\n header.find(SELECTORS.ACTION_REQUEST_BLOCK).addClass('hidden');\n header.find(SELECTORS.ACTION_REQUEST_UNBLOCK).removeClass('hidden');\n } else {\n header.find(SELECTORS.ACTION_REQUEST_BLOCK).removeClass('hidden');\n header.find(SELECTORS.ACTION_REQUEST_UNBLOCK).addClass('hidden');\n }\n };\n\n /**\n * Show or hide the favourite / unfavourite option in the header dropdown menu\n * and the favourite star in the header title.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} state is this conversation a favourite.\n */\n var renderIsFavourite = function(header, body, footer, state) {\n var favouriteIcon = header.find(SELECTORS.FAVOURITE_ICON_CONTAINER);\n var addFavourite = header.find(SELECTORS.ACTION_CONFIRM_FAVOURITE);\n var removeFavourite = header.find(SELECTORS.ACTION_CONFIRM_UNFAVOURITE);\n\n switch (state) {\n case 'hide':\n favouriteIcon.addClass('hidden');\n addFavourite.addClass('hidden');\n removeFavourite.addClass('hidden');\n break;\n case 'show-add':\n favouriteIcon.addClass('hidden');\n addFavourite.removeClass('hidden');\n removeFavourite.addClass('hidden');\n break;\n case 'show-remove':\n favouriteIcon.removeClass('hidden');\n addFavourite.addClass('hidden');\n removeFavourite.removeClass('hidden');\n break;\n }\n };\n\n /**\n * Show or hide the mute / unmute option in the header dropdown menu\n * and the muted icon in the header title.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {string} state The state of the conversation as defined by the patcher.\n */\n var renderIsMuted = function(header, body, footer, state) {\n var muteIcon = header.find(SELECTORS.MUTED_ICON_CONTAINER);\n var setMuted = header.find(SELECTORS.ACTION_CONFIRM_MUTE);\n var unsetMuted = header.find(SELECTORS.ACTION_CONFIRM_UNMUTE);\n\n switch (state) {\n case 'hide':\n muteIcon.addClass('hidden');\n setMuted.addClass('hidden');\n unsetMuted.addClass('hidden');\n break;\n case 'show-mute':\n muteIcon.addClass('hidden');\n setMuted.removeClass('hidden');\n unsetMuted.addClass('hidden');\n break;\n case 'show-unmute':\n muteIcon.removeClass('hidden');\n setMuted.addClass('hidden');\n unsetMuted.removeClass('hidden');\n break;\n }\n };\n\n /**\n * Show or hide the add / remove user as contact option in the header dropdown menu.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} state the contact state.\n */\n var renderIsContact = function(header, body, footer, state) {\n var addContact = header.find(SELECTORS.ACTION_REQUEST_ADD_CONTACT);\n var removeContact = header.find(SELECTORS.ACTION_REQUEST_REMOVE_CONTACT);\n\n switch (state) {\n case 'pending-contact':\n addContact.addClass('hidden');\n removeContact.addClass('hidden');\n break;\n case 'contact':\n addContact.addClass('hidden');\n removeContact.removeClass('hidden');\n break;\n case 'non-contact':\n addContact.removeClass('hidden');\n removeContact.addClass('hidden');\n break;\n }\n };\n\n /**\n * Show or hide confirm action from confirm dialogue is loading.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} isLoading confirm action is loading.\n */\n var renderLoadingConfirmAction = function(header, body, footer, isLoading) {\n var dialogue = getConfirmDialogueContainer(body);\n var buttons = dialogue.find('button');\n var buttonText = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_BUTTON_TEXT);\n var loadingIcon = dialogue.find(SELECTORS.LOADING_ICON_CONTAINER);\n\n if (isLoading) {\n buttons.prop('disabled', true);\n buttonText.addClass('hidden');\n loadingIcon.removeClass('hidden');\n } else {\n buttons.prop('disabled', false);\n buttonText.removeClass('hidden');\n loadingIcon.addClass('hidden');\n }\n };\n\n /**\n * Show or hide the header and footer content for edit mode.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Bool} inEditMode In edit mode or not.\n */\n var renderInEditMode = function(header, body, footer, inEditMode) {\n var messages = null;\n\n if (inEditMode) {\n messages = body.find(SELECTORS.MESSAGE_NOT_SELECTED);\n messages.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).removeClass('hidden');\n hideHeaderContent(header);\n showHeaderEditMode(header);\n } else {\n messages = getMessagesContainer(body);\n messages.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).addClass('hidden');\n messages.find(SELECTORS.MESSAGE_SELECTED_ICON).addClass('hidden');\n showHeaderContent(header);\n hideHeaderEditMode(header);\n }\n };\n\n /**\n * Select or unselect messages.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data The messages to select or unselect.\n */\n var renderSelectedMessages = function(header, body, footer, data) {\n var hasSelectedMessages = data.count > 0;\n\n if (data.add.length) {\n data.add.forEach(function(messageId) {\n var message = getMessageElement(body, messageId);\n message.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).addClass('hidden');\n message.find(SELECTORS.MESSAGE_SELECTED_ICON).removeClass('hidden');\n message.attr('aria-checked', true);\n });\n }\n\n if (data.remove.length) {\n data.remove.forEach(function(messageId) {\n var message = getMessageElement(body, messageId);\n\n if (hasSelectedMessages) {\n message.find(SELECTORS.MESSAGE_NOT_SELECTED_ICON).removeClass('hidden');\n }\n\n message.find(SELECTORS.MESSAGE_SELECTED_ICON).addClass('hidden');\n message.attr('aria-checked', false);\n });\n }\n\n setMessagesSelectedCount(header, data.count);\n };\n\n /**\n * Show or hide the require add contact panel.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} data Whether the user has to be added a a contact.\n * @return {Object} jQuery promise\n */\n var renderRequireAddContact = function(header, body, footer, data) {\n if (data.show && !data.hasMessages) {\n return Str.get_strings([\n {\n key: 'requirecontacttomessage',\n component: 'core_message',\n param: data.user.fullname\n },\n {\n key: 'isnotinyourcontacts',\n component: 'core_message',\n param: data.user.fullname\n }\n ])\n .then(function(strings) {\n var title = strings[1];\n var text = strings[0];\n return showConfirmDialogue(\n header,\n body,\n footer,\n [SELECTORS.ACTION_REQUEST_ADD_CONTACT],\n text,\n title,\n false,\n true\n );\n });\n } else {\n return hideConfirmDialogue(header, body, footer);\n }\n };\n\n /**\n * Show or hide the self-conversation message.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} displayMessage should the message be displayed?.\n * @return {Object|true} jQuery promise\n */\n var renderSelfConversationMessage = function(header, body, footer, displayMessage) {\n var container = getSelfConversationMessageContainer(body);\n if (displayMessage) {\n container.removeClass('hidden');\n } else {\n container.addClass('hidden');\n }\n return true;\n };\n\n /**\n * Show or hide the unable to message container and enable or disable the footer messaging controls.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {Object} disable should the message be displayed?.\n * @return {Object|true} jQuery promise\n */\n var renderUnableToMessage = function(header, body, footer, disable) {\n if (disable) {\n disableAllFooterContentElements(footer);\n if (!body.find(SELECTORS.DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER).length) {\n showUnableToMessage(body);\n }\n } else {\n enableAllFooterContentElements(footer);\n hideUnableToMessage(body);\n }\n\n return true;\n };\n\n /**\n * Show or hide the require add contact panel.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @param {String} userFullName Full name of the other user.\n * @return {Object|true} jQuery promise\n */\n var renderContactRequestSent = function(header, body, footer, userFullName) {\n var container = getContactRequestSentContainer(body);\n if (userFullName) {\n return Str.get_string('yourcontactrequestpending', 'core_message', truncateUsername(userFullName))\n .then(function(string) {\n container.find(SELECTORS.TEXT).text(string);\n container.removeClass('hidden');\n return string;\n });\n } else {\n container.addClass('hidden');\n return true;\n }\n };\n\n /**\n * Reset the UI to the initial state.\n *\n * @param {Object} header The header container element.\n * @param {Object} body The body container element.\n * @param {Object} footer The footer container element.\n * @return {Bool}\n */\n var renderReset = function(header, body, footer) {\n hideConfirmDialogue(header, body, footer);\n hideContactRequestSentContainer(body);\n hideSelfConversationMessageContainer(body);\n hideAllHeaderElements(header);\n showHeaderPlaceholder(header);\n hideAllFooterElements(footer);\n showFooterPlaceholder(footer);\n enableAllFooterContentElements(footer);\n hideUnableToMessage(body);\n return true;\n };\n\n var render = function(header, body, footer, patch) {\n var configs = [\n {\n // Resetting the UI needs to come first, if it's required.\n reset: renderReset\n },\n {\n // Any async rendering (stuff that requires templates, strings etc) should\n // go in here.\n conversation: renderConversation,\n header: renderHeader,\n footer: renderFooter,\n confirmBlockUser: renderConfirmBlockUser,\n confirmUnblockUser: renderConfirmUnblockUser,\n confirmAddContact: renderConfirmAddContact,\n confirmRemoveContact: renderConfirmRemoveContact,\n confirmDeleteSelectedMessages: renderConfirmDeleteSelectedMessages,\n confirmDeleteConversation: renderConfirmDeleteConversation,\n confirmContactRequest: renderConfirmContactRequest,\n requireAddContact: renderRequireAddContact,\n selfConversationMessage: renderSelfConversationMessage,\n contactRequestSent: renderContactRequestSent,\n unableToMessage: renderUnableToMessage,\n },\n {\n loadingMembers: renderLoadingMembers,\n loadingFirstMessages: renderLoadingFirstMessages,\n loadingMessages: renderLoadingMessages,\n isBlocked: renderIsBlocked,\n isContact: renderIsContact,\n isFavourite: renderIsFavourite,\n isMuted: renderIsMuted,\n loadingConfirmAction: renderLoadingConfirmAction,\n inEditMode: renderInEditMode,\n showEmojiPicker: renderShowEmojiPicker,\n showEmojiAutoComplete: renderShowEmojiAutoComplete,\n },\n {\n // Scrolling should be last to make sure everything\n // on the page is visible.\n scrollToMessage: renderScrollToMessage,\n selectedMessages: renderSelectedMessages\n }\n ];\n // Helper function to process each of the configs above.\n var processConfig = function(config) {\n var results = [];\n\n for (var key in patch) {\n if (config.hasOwnProperty(key)) {\n var renderFunc = config[key];\n var patchValue = patch[key];\n results.push(renderFunc(header, body, footer, patchValue));\n }\n }\n\n return results;\n };\n\n // The first config is special because it resets the UI.\n var renderingPromises = processConfig(configs[0]);\n // The second config is special because it contains async rendering.\n renderingPromises = renderingPromises.concat(processConfig(configs[1]));\n\n // Wait for the async rendering to complete before processing the\n // rest of the configs, in order.\n return $.when.apply($, renderingPromises)\n .then(function() {\n for (var i = 2; i < configs.length; i++) {\n processConfig(configs[i]);\n }\n\n return;\n })\n .catch(Notification.exception);\n };\n\n /**\n * Truncate long usernames.\n *\n * @param {String} username Text to truncate.\n * @return {String} Truncated string.\n */\n var truncateUsername = function(username) {\n return Truncate.truncate(username, {\n length: 30,\n words: true,\n ellipsis: '...'\n });\n };\n\n return {\n render: render,\n };\n});\n"],"names":["define","$","Notification","Str","Templates","UserDate","Constants","Aria","Truncate","SELECTORS","TEMPLATES","CONVERSATION_TYPES","getMessagesContainer","body","find","CONTENT_MESSAGES_CONTAINER","getSelfConversationMessageContainer","SELF_CONVERSATION_MESSAGE_CONTAINER","getUnableToMessageContainer","UNABLE_TO_MESSAGE_CONTAINER","getMessageTextArea","footer","MESSAGE_TEXT_AREA","getSendMessageButton","SEND_MESSAGE_BUTTON","getEmojiPickerButton","TOGGLE_EMOJI_PICKER_BUTTON","getContactRequestSentContainer","CONTACT_REQUEST_SENT_MESSAGE_CONTAINER","getFooterContentContainer","CONTENT_MESSAGES_FOOTER_CONTAINER","getFooterEditModeContainer","CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER","getFooterPlaceholderContainer","PLACEHOLDER_CONTAINER","showFooterPlaceholder","removeClass","getFooterRequireContactContainer","CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER","getFooterRequireUnblockContainer","CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER","hideAllFooterElements","addClass","hideFooterContent","hideFooterEditMode","hideFooterPlaceholder","hideFooterRequireContact","hideFooterRequireUnblock","getContentPlaceholderContainer","CONTENT_PLACEHOLDER_CONTAINER","getHeaderContent","header","HEADER","showHeaderContent","hideHeaderContent","hideUnableToMessage","enableAllFooterContentElements","prop","enableEmojiPickerButton","enableSendMessageButton","messageTextArea","unhide","get","get_string","done","string","enableMessageTextArea","disableAllFooterContentElements","disableEmojiPickerButton","disableSendMessageButton","removeAttr","disableMessageTextArea","getHeaderEditMode","HEADER_EDIT_MODE","hideHeaderEditMode","getHeaderPlaceholderContainer","HEADER_PLACEHOLDER_CONTAINER","showHeaderPlaceholder","hideHeaderPlaceholder","getMessageElement","messageId","getDayElement","dayTimeCreated","getMoreMessagesLoadingIconContainer","MORE_MESSAGES_LOADING_ICON_CONTAINER","getConfirmDialogueContainer","root","CONFIRM_DIALOGUE_CONTAINER","showConfirmDialogueContainer","container","siblings","hide","attr","hideConfirmDialogueContainer","formatMessagesForTemplate","messages","datesCache","map","message","id","isread","isRead","fromloggedinuser","fromLoggedInUser","userfrom","userFrom","text","formattedtime","timeCreated","renderConversation","data","renderingPromises","hasAddDays","days","add","length","hasAddMessages","hasUpdateMessages","update","timestampsToFormat","datesCachePromise","Deferred","resolve","promise","concat","reduce","carry","day","value","timestamps","push","before","after","then","format","requests","timestamp","formattedTimes","index","isAbleToMessage","messagesContainer","daysRenderPromises","timestampDate","Date","render","DAY","currentyear","getFullYear","when","apply","forEach","html","element","insertBefore","append","always","showUnableToMessage","DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER","last","toggleClass","catch","renderAddDays","unableToMessage","messagesData","formattedMessages","MESSAGES","messageList","messageHtml","DAY_MESSAGES_CONTAINER","renderAddMessages","formattedTime","LOADING_ICON_CONTAINER","TIME_CREATED","sendState","loading","time","retry","RETRY_SEND","TEXT_CONTAINER","errorMessage","messageContainer","ERROR_MESSAGE_CONTAINER","ERROR_MESSAGE","renderUpdateMessages","remove","renderRemoveDays","renderRemoveMessages","renderHeader","headerContainer","template","HEADER_PUBLIC","context","showrouteback","type","PRIVATE","showControls","HEADER_PRIVATE","HEADER_PRIVATE_NO_CONTROLS","SELF","HEADER_SELF","js","replaceNodeContents","renderFooter","get_strings","key","component","param","user","fullname","strings","title","footerContainer","TITLE","TEXT","showFooterRequireContact","showFooterEditMode","showFooterContent","showFooterRequireUnblock","renderScrollToMessage","position","scrollTop","top","renderLoadingMembers","isLoadingMembers","renderLoadingFirstMessages","isLoadingFirstMessages","hideMessagesContainer","showContentPlaceholder","showMessagesContainer","hideContentPlaceholder","renderLoadingMessages","isLoading","showMoreMessagesLoadingIcon","hideMoreMessagesLoadingIcon","renderShowEmojiPicker","show","EMOJI_PICKER_CONTAINER","getEmojiPickerContainer","EMOJI_PICKER_SEARCH_INPUT","focus","renderShowEmojiAutoComplete","EMOJI_AUTO_COMPLETE_CONTAINER","getEmojiAutoCompleteContainer","showConfirmDialogue","buttonSelectors","bodyText","headerText","canCancel","skipHeader","showOk","dialogue","buttons","selector","cancelButton","CONFIRM_DIALOGUE_CANCEL_BUTTON","okayButton","CONFIRM_DIALOGUE_OKAY_BUTTON","CONFIRM_DIALOGUE_TEXT","dialogueHeader","CONFIRM_DIALOGUE_HEADER","CONFIRM_DIALOGUE","prepend","button","CAN_RECEIVE_FOCUS","filter","first","hideConfirmDialogue","hideCheckDeleteDialogue","renderConfirmBlockUser","username","truncateUsername","canmessageevenifblocked","ACTION_CONFIRM_BLOCK","renderConfirmUnblockUser","ACTION_CONFIRM_UNBLOCK","renderConfirmAddContact","userFullName","truncate","words","ellipsis","ACTION_CONFIRM_ADD_CONTACT","renderConfirmRemoveContact","ACTION_CONFIRM_REMOVE_CONTACT","renderConfirmDeleteSelectedMessages","showmessage","canDeleteMessagesForAllUsers","showCheckDeleteDialogue","ACTION_CONFIRM_DELETE_SELECTED_MESSAGES","renderConfirmDeleteConversation","ACTION_CONFIRM_DELETE_CONVERSATION","renderConfirmContactRequest","ACTION_ACCEPT_CONTACT_REQUEST","ACTION_DECLINE_CONTACT_REQUEST","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER","checkboxRegion","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE","renderIsBlocked","isBlocked","ACTION_REQUEST_BLOCK","ACTION_REQUEST_UNBLOCK","renderIsFavourite","state","favouriteIcon","FAVOURITE_ICON_CONTAINER","addFavourite","ACTION_CONFIRM_FAVOURITE","removeFavourite","ACTION_CONFIRM_UNFAVOURITE","renderIsMuted","muteIcon","MUTED_ICON_CONTAINER","setMuted","ACTION_CONFIRM_MUTE","unsetMuted","ACTION_CONFIRM_UNMUTE","renderIsContact","addContact","ACTION_REQUEST_ADD_CONTACT","removeContact","ACTION_REQUEST_REMOVE_CONTACT","renderLoadingConfirmAction","buttonText","CONFIRM_DIALOGUE_BUTTON_TEXT","loadingIcon","renderInEditMode","inEditMode","MESSAGE_NOT_SELECTED","MESSAGE_NOT_SELECTED_ICON","showHeaderEditMode","MESSAGE_SELECTED_ICON","renderSelectedMessages","hasSelectedMessages","count","MESSAGES_SELECTED_COUNT","setMessagesSelectedCount","renderRequireAddContact","hasMessages","renderSelfConversationMessage","displayMessage","renderUnableToMessage","disable","renderContactRequestSent","renderReset","hideContactRequestSentContainer","hideSelfConversationMessageContainer","hideAllHeaderElements","patch","configs","reset","conversation","confirmBlockUser","confirmUnblockUser","confirmAddContact","confirmRemoveContact","confirmDeleteSelectedMessages","confirmDeleteConversation","confirmContactRequest","requireAddContact","selfConversationMessage","contactRequestSent","loadingMembers","loadingFirstMessages","loadingMessages","isContact","isFavourite","isMuted","loadingConfirmAction","showEmojiPicker","showEmojiAutoComplete","scrollToMessage","selectedMessages","processConfig","config","results","hasOwnProperty","renderFunc","patchValue","i","exception"],"mappings":"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"} \ No newline at end of file diff --git a/public/message/amd/build/message_drawer_view_conversation_state_manager.min.js b/public/message/amd/build/message_drawer_view_conversation_state_manager.min.js index f2590d29827..23bf687ffb0 100644 --- a/public/message/amd/build/message_drawer_view_conversation_state_manager.min.js +++ b/public/message/amd/build/message_drawer_view_conversation_state_manager.min.js @@ -15,6 +15,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("core_message/message_drawer_view_conversation_state_manager",["jquery"],(function($){var cloneState=function(state){return $.extend(!0,{},state)},formatMessages=function(messages,loggedInUserId,members){return messages.map((function(message){var fromLoggedInUser=message.useridfrom==loggedInUserId;return{id:""+message.id,fromLoggedInUser:fromLoggedInUser,userFrom:members[message.useridfrom],text:message.text,timeCreated:message.timecreated?parseInt(message.timecreated,10):null}}))};return{buildInitialState:function(midnight,loggedInUserId,id,messagePollMin,messagePollMax,messagePollAfterMax){return{midnight:midnight,loggedInUserId:loggedInUserId,id:id,messagePollMin:messagePollMin,messagePollMax:messagePollMax,messagePollAfterMax:messagePollAfterMax,name:null,subname:null,type:null,totalMemberCount:null,imageUrl:null,isFavourite:null,isMuted:null,canDeleteMessagesForAllUsers:!1,deleteMessagesForAllUsers:!1,members:{},messages:[],hasTriedToLoadMessages:!1,loadingMessages:!0,loadingMembers:!0,loadingConfirmAction:!1,pendingBlockUserIds:[],pendingUnblockUserIds:[],pendingRemoveContactIds:[],pendingAddContactIds:[],pendingDeleteMessageIds:[],pendingSendMessageIds:[],pendingDeleteConversation:!1,selectedMessageIds:[],showEmojiAutoComplete:!1,showEmojiPicker:!1}},addMessages:function(state,messages){var newState=cloneState(state),formattedMessages=formatMessages(messages,state.loggedInUserId,state.members);formattedMessages=formattedMessages.map((function(message){return message.sendState=null,message.timeAdded=Date.now(),message.errorMessage=null,message}));var allMessages=state.messages.concat(formattedMessages);return allMessages.sort((function(a,b){if(null===a.timeCreated&&null===b.timeCreated){if(a.timeAddedb.timeAdded)return 1}return null===a.timeCreated&&null!==b.timeCreated?1:null!==a.timeCreated&&null===b.timeCreated||a.timeCreatedb.timeCreated?1:a.idb.id?1:0})),newState.messages=allMessages.filter((function(message,index,sortedMessages){return!index||message.id!=sortedMessages[index-1].id})),newState},updateMessages:function(state,data){var newState=cloneState(state),updatesById=data.reduce((function(carry,messageData){var oldMessage=messageData[0],newMessage=messageData[1],formattedMessage=formatMessages([newMessage],state.loggedInUserId,state.members)[0];return carry[oldMessage.id]=formattedMessage,carry}),{});return newState.messages=newState.messages.map((function(message){return message.id in updatesById?$.extend(message,updatesById[message.id]):message})),newState},removeMessages:function(state,messages){var newState=cloneState(state),removeMessageIds=messages.map((function(message){return""+message.id}));return newState.messages=newState.messages.filter((function(message){return removeMessageIds.indexOf(message.id)<0})),newState},removeMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.messages=newState.messages.filter((function(message){return messageIds.indexOf(message.id)<0})),newState},addMembers:function(state,members){var newState=cloneState(state),formattedMembers=function(members){return members.map((function(member){return{id:member.id,fullname:member.fullname,profileurl:member.profileurl,profileimageurl:member.profileimageurl,profileimageurlsmall:member.profileimageurlsmall,isonline:member.isonline,showonlinestatus:member.showonlinestatus,isblocked:member.isblocked,iscontact:member.iscontact,isdeleted:member.isdeleted,canmessage:member.canmessage,canmessageevenifblocked:member.canmessageevenifblocked,requirescontact:member.requirescontact,contactrequests:member.contactrequests||[],cancreatecontact:member.cancreatecontact}}))}(members);return formattedMembers.forEach((function(member){newState.members[member.id]=member})),newState},removeMembers:function(state,members){var newState=cloneState(state);return members.forEach((function(member){delete newState.members[member.id]})),newState},setLoadingMessages:function(state,value){var newState=cloneState(state);return newState.loadingMessages=value,state.loadingMessages&&!value&&(newState.hasTriedToLoadMessages=!0),newState},setLoadingMembers:function(state,value){var newState=cloneState(state);return newState.loadingMembers=value,newState},setId:function(state,value){var newState=cloneState(state);return newState.id=value,newState},setName:function(state,value){var newState=cloneState(state);return newState.name=value,newState},setSubname:function(state,value){var newState=cloneState(state);return newState.subname=value,newState},setType:function(state,type){var newState=cloneState(state);return newState.type=type,newState},setIsFavourite:function(state,isFavourite){var newState=cloneState(state);return newState.isFavourite=isFavourite,newState},setIsMuted:function(state,isMuted){var newState=cloneState(state);return newState.isMuted=isMuted,newState},setCanDeleteMessagesForAllUsers:function(state,value){var newState=cloneState(state);return newState.canDeleteMessagesForAllUsers=value,newState},setDeleteMessagesForAllUsers:function(state,value){var newState=cloneState(state);return newState.deleteMessagesForAllUsers=value,newState},setTotalMemberCount:function(state,count){var newState=cloneState(state);return newState.totalMemberCount=count,newState},setImageUrl:function(state,url){var newState=cloneState(state);return newState.imageUrl=url,newState},setLoadingConfirmAction:function(state,value){var newState=cloneState(state);return newState.loadingConfirmAction=value,newState},setPendingDeleteConversation:function(state,value){var newState=cloneState(state);return newState.pendingDeleteConversation=value,newState},setMessagesSendPendingById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.messages.forEach((function(message){messageIds.indexOf(message.id)>=0&&(message.sendState="pending",message.errorMessage=null)})),newState},setMessagesSendSuccessById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.messages.forEach((function(message){messageIds.indexOf(message.id)>=0&&(message.sendState="sent",message.errorMessage=null)})),newState},setMessagesSendFailById:function(state,messageIds,errorMessage){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.messages.forEach((function(message){messageIds.indexOf(message.id)>=0&&(message.sendState="error",message.errorMessage=errorMessage)})),newState},setShowEmojiAutoComplete:function(state,show){var newState=cloneState(state);return newState.showEmojiAutoComplete=show,newState},setShowEmojiPicker:function(state,show){var newState=cloneState(state);return newState.showEmojiPicker=show,newState},addPendingBlockUsersById:function(state,userIds){var newState=cloneState(state);return userIds.forEach((function(id){newState.pendingBlockUserIds.push(id)})),newState},addPendingRemoveContactsById:function(state,userIds){var newState=cloneState(state);return userIds.forEach((function(id){newState.pendingRemoveContactIds.push(id)})),newState},addPendingUnblockUsersById:function(state,userIds){var newState=cloneState(state);return userIds.forEach((function(id){newState.pendingUnblockUserIds.push(id)})),newState},addPendingAddContactsById:function(state,userIds){var newState=cloneState(state);return userIds.forEach((function(id){newState.pendingAddContactIds.push(id)})),newState},addPendingDeleteMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds.forEach((function(id){newState.pendingDeleteMessageIds.push(id)})),newState},removePendingBlockUsersById:function(state,userIds){var newState=cloneState(state);return newState.pendingBlockUserIds=newState.pendingBlockUserIds.filter((function(id){return userIds.indexOf(id)<0})),newState},removePendingRemoveContactsById:function(state,userIds){var newState=cloneState(state);return newState.pendingRemoveContactIds=newState.pendingRemoveContactIds.filter((function(id){return userIds.indexOf(id)<0})),newState},removePendingUnblockUsersById:function(state,userIds){var newState=cloneState(state);return newState.pendingUnblockUserIds=newState.pendingUnblockUserIds.filter((function(id){return userIds.indexOf(id)<0})),newState},removePendingAddContactsById:function(state,userIds){var newState=cloneState(state);return newState.pendingAddContactIds=newState.pendingAddContactIds.filter((function(id){return userIds.indexOf(id)<0})),newState},removePendingDeleteMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.pendingDeleteMessageIds=newState.pendingDeleteMessageIds.filter((function(id){return messageIds.indexOf(id)<0})),newState},addSelectedMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.selectedMessageIds=newState.selectedMessageIds.concat(messageIds),newState},removeSelectedMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.selectedMessageIds=newState.selectedMessageIds.filter((function(id){return messageIds.indexOf(id)<0})),newState},markMessagesAsRead:function(state,readMessages){var newState=cloneState(state),readMessageIds=readMessages.map((function(message){return message.id}));return newState.messages=newState.messages.map((function(message){return readMessageIds.indexOf(message.id)>=0&&(message.isRead=!0),message})),newState},addContactRequests:function(state,requests){var newState=cloneState(state);return requests.forEach((function(request){var fromUserId=request.userid,toUserId=request.requesteduserid;newState.members[fromUserId].contactrequests.push(request),newState.members[toUserId].contactrequests.push(request)})),newState},removeContactRequests:function(state,requests){var newState=cloneState(state);return requests.forEach((function(request){var fromUserId=request.userid,toUserId=request.requesteduserid;newState.members[fromUserId].contactrequests=newState.members[fromUserId].contactrequests.filter((function(existing){return existing.userid!=fromUserId})),newState.members[toUserId].contactrequests=newState.members[toUserId].contactrequests.filter((function(existing){return existing.requesteduserid!=toUserId}))})),newState}}})); +define("core_message/message_drawer_view_conversation_state_manager",["jquery"],(function($){var cloneState=function(state){return $.extend(!0,{},state)},formatMessages=function(messages,loggedInUserId,members){return messages.map((function(message){var fromLoggedInUser=message.useridfrom==loggedInUserId;return{id:""+message.id,fromLoggedInUser:fromLoggedInUser,userFrom:members[message.useridfrom],text:message.text,timeCreated:message.timecreated?parseInt(message.timecreated,10):null}}))};return{buildInitialState:function(midnight,loggedInUserId,id,messagePollMin,messagePollMax,messagePollAfterMax){return{midnight:midnight,loggedInUserId:loggedInUserId,id:id,messagePollMin:messagePollMin,messagePollMax:messagePollMax,messagePollAfterMax:messagePollAfterMax,name:null,subname:null,type:null,totalMemberCount:null,imageUrl:null,isFavourite:null,isMuted:null,canDeleteMessagesForAllUsers:!1,deleteMessagesForAllUsers:!1,members:{},messages:[],hasTriedToLoadMessages:!1,loadingMessages:!0,loadingMembers:!0,loadingConfirmAction:!1,pendingBlockUserIds:[],pendingUnblockUserIds:[],pendingRemoveContactIds:[],pendingAddContactIds:[],pendingDeleteMessageIds:[],pendingSendMessageIds:[],pendingDeleteConversation:!1,selectedMessageIds:[],showEmojiAutoComplete:!1,showEmojiPicker:!1,canSendMessageToConversation:!0}},addMessages:function(state,messages){var newState=cloneState(state),formattedMessages=formatMessages(messages,state.loggedInUserId,state.members);formattedMessages=formattedMessages.map((function(message){return message.sendState=null,message.timeAdded=Date.now(),message.errorMessage=null,message}));var allMessages=state.messages.concat(formattedMessages);return allMessages.sort((function(a,b){if(null===a.timeCreated&&null===b.timeCreated){if(a.timeAddedb.timeAdded)return 1}return null===a.timeCreated&&null!==b.timeCreated?1:null!==a.timeCreated&&null===b.timeCreated||a.timeCreatedb.timeCreated?1:a.idb.id?1:0})),newState.messages=allMessages.filter((function(message,index,sortedMessages){return!index||message.id!=sortedMessages[index-1].id})),newState},updateMessages:function(state,data){var newState=cloneState(state),updatesById=data.reduce((function(carry,messageData){var oldMessage=messageData[0],newMessage=messageData[1],formattedMessage=formatMessages([newMessage],state.loggedInUserId,state.members)[0];return carry[oldMessage.id]=formattedMessage,carry}),{});return newState.messages=newState.messages.map((function(message){return message.id in updatesById?$.extend(message,updatesById[message.id]):message})),newState},removeMessages:function(state,messages){var newState=cloneState(state),removeMessageIds=messages.map((function(message){return""+message.id}));return newState.messages=newState.messages.filter((function(message){return removeMessageIds.indexOf(message.id)<0})),newState},removeMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.messages=newState.messages.filter((function(message){return messageIds.indexOf(message.id)<0})),newState},addMembers:function(state,members){var newState=cloneState(state),formattedMembers=function(members){return members.map((function(member){return{id:member.id,fullname:member.fullname,profileurl:member.profileurl,profileimageurl:member.profileimageurl,profileimageurlsmall:member.profileimageurlsmall,isonline:member.isonline,showonlinestatus:member.showonlinestatus,isblocked:member.isblocked,iscontact:member.iscontact,isdeleted:member.isdeleted,canmessage:member.canmessage,canmessageevenifblocked:member.canmessageevenifblocked,requirescontact:member.requirescontact,contactrequests:member.contactrequests||[],cancreatecontact:member.cancreatecontact}}))}(members);return formattedMembers.forEach((function(member){newState.members[member.id]=member})),newState},removeMembers:function(state,members){var newState=cloneState(state);return members.forEach((function(member){delete newState.members[member.id]})),newState},setLoadingMessages:function(state,value){var newState=cloneState(state);return newState.loadingMessages=value,state.loadingMessages&&!value&&(newState.hasTriedToLoadMessages=!0),newState},setLoadingMembers:function(state,value){var newState=cloneState(state);return newState.loadingMembers=value,newState},setId:function(state,value){var newState=cloneState(state);return newState.id=value,newState},setName:function(state,value){var newState=cloneState(state);return newState.name=value,newState},setSubname:function(state,value){var newState=cloneState(state);return newState.subname=value,newState},setType:function(state,type){var newState=cloneState(state);return newState.type=type,newState},setIsFavourite:function(state,isFavourite){var newState=cloneState(state);return newState.isFavourite=isFavourite,newState},setIsMuted:function(state,isMuted){var newState=cloneState(state);return newState.isMuted=isMuted,newState},setCanDeleteMessagesForAllUsers:function(state,value){var newState=cloneState(state);return newState.canDeleteMessagesForAllUsers=value,newState},setDeleteMessagesForAllUsers:function(state,value){var newState=cloneState(state);return newState.deleteMessagesForAllUsers=value,newState},setTotalMemberCount:function(state,count){var newState=cloneState(state);return newState.totalMemberCount=count,newState},setImageUrl:function(state,url){var newState=cloneState(state);return newState.imageUrl=url,newState},setLoadingConfirmAction:function(state,value){var newState=cloneState(state);return newState.loadingConfirmAction=value,newState},setPendingDeleteConversation:function(state,value){var newState=cloneState(state);return newState.pendingDeleteConversation=value,newState},setMessagesSendPendingById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.messages.forEach((function(message){messageIds.indexOf(message.id)>=0&&(message.sendState="pending",message.errorMessage=null)})),newState},setMessagesSendSuccessById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.messages.forEach((function(message){messageIds.indexOf(message.id)>=0&&(message.sendState="sent",message.errorMessage=null)})),newState},setMessagesSendFailById:function(state,messageIds,errorMessage){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.messages.forEach((function(message){messageIds.indexOf(message.id)>=0&&(message.sendState="error",message.errorMessage=errorMessage)})),newState},setShowEmojiAutoComplete:function(state,show){var newState=cloneState(state);return newState.showEmojiAutoComplete=show,newState},setShowEmojiPicker:function(state,show){var newState=cloneState(state);return newState.showEmojiPicker=show,newState},addPendingBlockUsersById:function(state,userIds){var newState=cloneState(state);return userIds.forEach((function(id){newState.pendingBlockUserIds.push(id)})),newState},addPendingRemoveContactsById:function(state,userIds){var newState=cloneState(state);return userIds.forEach((function(id){newState.pendingRemoveContactIds.push(id)})),newState},addPendingUnblockUsersById:function(state,userIds){var newState=cloneState(state);return userIds.forEach((function(id){newState.pendingUnblockUserIds.push(id)})),newState},addPendingAddContactsById:function(state,userIds){var newState=cloneState(state);return userIds.forEach((function(id){newState.pendingAddContactIds.push(id)})),newState},addPendingDeleteMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds.forEach((function(id){newState.pendingDeleteMessageIds.push(id)})),newState},removePendingBlockUsersById:function(state,userIds){var newState=cloneState(state);return newState.pendingBlockUserIds=newState.pendingBlockUserIds.filter((function(id){return userIds.indexOf(id)<0})),newState},removePendingRemoveContactsById:function(state,userIds){var newState=cloneState(state);return newState.pendingRemoveContactIds=newState.pendingRemoveContactIds.filter((function(id){return userIds.indexOf(id)<0})),newState},removePendingUnblockUsersById:function(state,userIds){var newState=cloneState(state);return newState.pendingUnblockUserIds=newState.pendingUnblockUserIds.filter((function(id){return userIds.indexOf(id)<0})),newState},removePendingAddContactsById:function(state,userIds){var newState=cloneState(state);return newState.pendingAddContactIds=newState.pendingAddContactIds.filter((function(id){return userIds.indexOf(id)<0})),newState},removePendingDeleteMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.pendingDeleteMessageIds=newState.pendingDeleteMessageIds.filter((function(id){return messageIds.indexOf(id)<0})),newState},addSelectedMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.selectedMessageIds=newState.selectedMessageIds.concat(messageIds),newState},removeSelectedMessagesById:function(state,messageIds){var newState=cloneState(state);return messageIds=messageIds.map((function(id){return""+id})),newState.selectedMessageIds=newState.selectedMessageIds.filter((function(id){return messageIds.indexOf(id)<0})),newState},markMessagesAsRead:function(state,readMessages){var newState=cloneState(state),readMessageIds=readMessages.map((function(message){return message.id}));return newState.messages=newState.messages.map((function(message){return readMessageIds.indexOf(message.id)>=0&&(message.isRead=!0),message})),newState},addContactRequests:function(state,requests){var newState=cloneState(state);return requests.forEach((function(request){var fromUserId=request.userid,toUserId=request.requesteduserid;newState.members[fromUserId].contactrequests.push(request),newState.members[toUserId].contactrequests.push(request)})),newState},removeContactRequests:function(state,requests){var newState=cloneState(state);return requests.forEach((function(request){var fromUserId=request.userid,toUserId=request.requesteduserid;newState.members[fromUserId].contactrequests=newState.members[fromUserId].contactrequests.filter((function(existing){return existing.userid!=fromUserId})),newState.members[toUserId].contactrequests=newState.members[toUserId].contactrequests.filter((function(existing){return existing.requesteduserid!=toUserId}))})),newState},setCanSendMessageToConversation:function(state,value){const newState=cloneState(state);return newState.canSendMessageToConversation=value,newState}}})); //# sourceMappingURL=message_drawer_view_conversation_state_manager.min.js.map \ No newline at end of file diff --git a/public/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map b/public/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map index a2f4f7e45e4..cd72f4d1c70 100644 --- a/public/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map +++ b/public/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map @@ -1 +1 @@ -{"version":3,"file":"message_drawer_view_conversation_state_manager.min.js","sources":["../src/message_drawer_view_conversation_state_manager.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 .\n\n/**\n * This module operates on the view states from the message_drawer_view_conversation module.\n * It exposes functions that can be used to generate new version of the state.\n *\n * Important notes for this module:\n * 1.) The existing state is always immutable. It should never be modified.\n * 2.) All functions that operate on the state should always clone the state and\n * modify the cloned state before returning it.\n *\n * It's important that the states remain immutable because they are diff'd in\n * the message_drawer_view_conversation_patcher module in order to work out what\n * has changed.\n *\n * @module core_message/message_drawer_view_conversation_state_manager\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n /**\n * Clone a state, a state is a collection of information about the variables required to build\n * the conversation user interface.\n *\n * @param {Object} state State to clone\n * @return {Object} newstate A copy of the state to clone.\n */\n var cloneState = function(state) {\n // Do a deep extend to make sure we recursively copy objects and\n // arrays so that the new state doesn't contain any references to\n // the old state, e.g. adding a value to an array in the new state\n // shouldn't also add it to the old state.\n return $.extend(true, {}, state);\n };\n\n /**\n * Format messages to be used in a state.\n *\n * @param {Array} messages The messages to format.\n * @param {Number} loggedInUserId The logged in user id.\n * @param {Array} members The converstation members.\n * @return {Array} Formatted messages.\n */\n var formatMessages = function(messages, loggedInUserId, members) {\n return messages.map(function(message) {\n var fromLoggedInUser = message.useridfrom == loggedInUserId;\n return {\n // Stringify the id.\n id: \"\" + message.id,\n fromLoggedInUser: fromLoggedInUser,\n userFrom: members[message.useridfrom],\n text: message.text,\n timeCreated: message.timecreated ? parseInt(message.timecreated, 10) : null\n };\n });\n };\n\n /**\n * Format members to be used in a state.\n *\n * @param {Array} members The messages to format.\n * @return {Array} Formatted members.\n */\n var formatMembers = function(members) {\n return members.map(function(member) {\n return {\n id: member.id,\n fullname: member.fullname,\n profileurl: member.profileurl,\n profileimageurl: member.profileimageurl,\n profileimageurlsmall: member.profileimageurlsmall,\n isonline: member.isonline,\n showonlinestatus: member.showonlinestatus,\n isblocked: member.isblocked,\n iscontact: member.iscontact,\n isdeleted: member.isdeleted,\n canmessage: member.canmessage,\n canmessageevenifblocked: member.canmessageevenifblocked,\n requirescontact: member.requirescontact,\n contactrequests: member.contactrequests || [],\n cancreatecontact: member.cancreatecontact,\n };\n });\n };\n\n /**\n * Create an initial (blank) state.\n *\n * @param {Number} midnight Midnight time.\n * @param {Number} loggedInUserId The logged in user id.\n * @param {Number} id The conversation id.\n * @param {Number} messagePollMin The message poll start timeout in seconds.\n * @param {Number} messagePollMax The message poll max timeout limit in seconds.\n * @param {Number} messagePollAfterMax The message poll frequency in seconds to reset to after max limit is reached.\n * @return {Object} Initial state.\n */\n var buildInitialState = function(\n midnight,\n loggedInUserId,\n id,\n messagePollMin,\n messagePollMax,\n messagePollAfterMax\n ) {\n return {\n midnight: midnight,\n loggedInUserId: loggedInUserId,\n id: id,\n messagePollMin: messagePollMin,\n messagePollMax: messagePollMax,\n messagePollAfterMax: messagePollAfterMax,\n name: null,\n subname: null,\n type: null,\n totalMemberCount: null,\n imageUrl: null,\n isFavourite: null,\n isMuted: null,\n canDeleteMessagesForAllUsers: false,\n deleteMessagesForAllUsers: false,\n members: {},\n messages: [],\n hasTriedToLoadMessages: false,\n loadingMessages: true,\n loadingMembers: true,\n loadingConfirmAction: false,\n pendingBlockUserIds: [],\n pendingUnblockUserIds: [],\n pendingRemoveContactIds: [],\n pendingAddContactIds: [],\n pendingDeleteMessageIds: [],\n pendingSendMessageIds: [],\n pendingDeleteConversation: false,\n selectedMessageIds: [],\n showEmojiAutoComplete: false,\n showEmojiPicker: false\n };\n };\n\n /**\n * Add messages to a state and sort them by timecreated.\n *\n * @param {Object} state Current state.\n * @param {Array} messages Messages to add to state.\n * @return {Object} state New state with added messages.\n */\n var addMessages = function(state, messages) {\n var newState = cloneState(state);\n var formattedMessages = formatMessages(messages, state.loggedInUserId, state.members);\n formattedMessages = formattedMessages.map(function(message) {\n message.sendState = null;\n message.timeAdded = Date.now();\n message.errorMessage = null;\n return message;\n });\n var allMessages = state.messages.concat(formattedMessages);\n // Sort the messages. Oldest to newest.\n allMessages.sort(function(a, b) {\n if (a.timeCreated === null && b.timeCreated === null) {\n if (a.timeAdded < b.timeAdded) {\n return -1;\n } else if (a.timeAdded > b.timeAdded) {\n return 1;\n }\n }\n\n if (a.timeCreated === null && b.timeCreated !== null) {\n // A comes after b.\n return 1;\n } else if (a.timeCreated !== null && b.timeCreated === null) {\n // A comes before b.\n return -1;\n } else if (a.timeCreated < b.timeCreated) {\n // A comes before b.\n return -1;\n } else if (a.timeCreated > b.timeCreated) {\n // A comes after b.\n return 1;\n } else if (a.id < b.id) {\n return -1;\n } else if (a.id > b.id) {\n return 1;\n } else {\n return 0;\n }\n });\n\n // Filter out any duplicate messages.\n newState.messages = allMessages.filter(function(message, index, sortedMessages) {\n return !index || message.id != sortedMessages[index - 1].id;\n });\n\n return newState;\n };\n\n /**\n * Update existing messages.\n *\n * @param {Object} state Current state.\n * @param {Array} data 2D array of old and new messages\n * @return {Object} state.\n */\n var updateMessages = function(state, data) {\n var newState = cloneState(state);\n var updatesById = data.reduce(function(carry, messageData) {\n var oldMessage = messageData[0];\n var newMessage = messageData[1];\n var formattedMessages = formatMessages([newMessage], state.loggedInUserId, state.members);\n var formattedMessage = formattedMessages[0];\n\n carry[oldMessage.id] = formattedMessage;\n return carry;\n }, {});\n\n newState.messages = newState.messages.map(function(message) {\n if (message.id in updatesById) {\n return $.extend(message, updatesById[message.id]);\n } else {\n return message;\n }\n });\n\n return newState;\n };\n\n /**\n * Remove messages from state.\n *\n * @param {Object} state Current state.\n * @param {Array} messages Messages to remove from state.\n * @return {Object} state New state with removed messages.\n */\n var removeMessages = function(state, messages) {\n var newState = cloneState(state);\n var removeMessageIds = messages.map(function(message) {\n return \"\" + message.id;\n });\n newState.messages = newState.messages.filter(function(message) {\n return removeMessageIds.indexOf(message.id) < 0;\n });\n\n return newState;\n };\n\n /**\n * Remove messages from state by message id.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Message ids to remove from state.\n * @return {Object} state New state with removed messages.\n */\n var removeMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.messages = newState.messages.filter(function(message) {\n return messageIds.indexOf(message.id) < 0;\n });\n\n return newState;\n };\n\n /**\n * Add conversation member to state.\n *\n * @param {Object} state Current state.\n * @param {Array} members Conversation members to be added to state.\n * @return {Object} New state with added members.\n */\n var addMembers = function(state, members) {\n var newState = cloneState(state);\n var formattedMembers = formatMembers(members);\n formattedMembers.forEach(function(member) {\n newState.members[member.id] = member;\n });\n return newState;\n };\n\n /**\n * Remove members from state.\n *\n * @param {Object} state Current state.\n * @param {Array} members Members to be removed from state.\n * @return {Object} New state with removed members.\n */\n var removeMembers = function(state, members) {\n var newState = cloneState(state);\n members.forEach(function(member) {\n delete newState.members[member.id];\n });\n return newState;\n };\n\n /**\n * Set the state loading messages attribute.\n *\n * @param {Object} state Current state.\n * @param {Bool} value New loading messages value.\n * @return {Object} New state with loading messages attribute.\n */\n var setLoadingMessages = function(state, value) {\n var newState = cloneState(state);\n newState.loadingMessages = value;\n if (state.loadingMessages && !value) {\n // If we're going from loading to not loading then\n // it means we've tried to load.\n newState.hasTriedToLoadMessages = true;\n }\n return newState;\n };\n\n /**\n * Set the state loading members attribute.\n *\n * @param {Object} state Current state.\n * @param {Bool} value New loading members value.\n * @return {Object} New state with loading members attribute.\n */\n var setLoadingMembers = function(state, value) {\n var newState = cloneState(state);\n newState.loadingMembers = value;\n return newState;\n };\n\n /**\n * Set the conversation id.\n *\n * @param {Object} state Current state.\n * @param {String} value The ID.\n * @return {Object} New state.\n */\n var setId = function(state, value) {\n var newState = cloneState(state);\n newState.id = value;\n return newState;\n };\n\n /**\n * Set the state name attribute.\n *\n * @param {Object} state Current state.\n * @param {String} value New name value.\n * @return {Object} New state with name attribute.\n */\n var setName = function(state, value) {\n var newState = cloneState(state);\n newState.name = value;\n return newState;\n };\n\n /**\n * Set the state subname attribute.\n *\n * @param {Object} state Current state.\n * @param {String} value New subname value.\n * @return {Object} New state.\n */\n var setSubname = function(state, value) {\n var newState = cloneState(state);\n newState.subname = value;\n return newState;\n };\n\n /**\n * Set the conversation type.\n *\n * @param {Object} state Current state.\n * @param {Int} type Conversation type.\n * @return {Object} New state.\n */\n var setType = function(state, type) {\n var newState = cloneState(state);\n newState.type = type;\n return newState;\n };\n\n /**\n * Set whether the conversation is a favourite conversation.\n *\n * @param {Object} state Current state.\n * @param {Bool} isFavourite If it's a favourite.\n * @return {Object} New state.\n */\n var setIsFavourite = function(state, isFavourite) {\n var newState = cloneState(state);\n newState.isFavourite = isFavourite;\n return newState;\n };\n\n /**\n * Set whether the conversation is a muted conversation.\n *\n * @param {Object} state Current state.\n * @param {bool} isMuted If it's muted.\n * @return {Object} New state.\n */\n var setIsMuted = function(state, isMuted) {\n var newState = cloneState(state);\n newState.isMuted = isMuted;\n return newState;\n };\n\n /**\n * Set the total member count.\n *\n * @param {Object} state Current state.\n * @param {String} count The count.\n * @return {Object} New state.\n */\n var setTotalMemberCount = function(state, count) {\n var newState = cloneState(state);\n newState.totalMemberCount = count;\n return newState;\n };\n\n /**\n * Set the conversation image url.\n *\n * @param {Object} state Current state.\n * @param {String} url The url to the image.\n * @return {Object} New state.\n */\n var setImageUrl = function(state, url) {\n var newState = cloneState(state);\n newState.imageUrl = url;\n return newState;\n };\n\n /**\n * Set the state loading confirm action attribute.\n *\n * @param {Object} state Current state.\n * @param {Bool} value New loading confirm action value.\n * @return {Object} New state with loading confirm action attribute.\n */\n var setLoadingConfirmAction = function(state, value) {\n var newState = cloneState(state);\n newState.loadingConfirmAction = value;\n return newState;\n };\n\n /**\n * Set the state pending delete conversation attribute.\n *\n * @param {Object} state Current state.\n * @param {Bool} value New pending delete conversation value.\n * @return {Object} New state with pending delete conversation attribute.\n */\n var setPendingDeleteConversation = function(state, value) {\n var newState = cloneState(state);\n newState.pendingDeleteConversation = value;\n return newState;\n };\n\n /**\n * Set the state of message to pending.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to delete.\n * @return {Object} New state with array of pending delete message ids.\n */\n var setMessagesSendPendingById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.messages.forEach(function(message) {\n if (messageIds.indexOf(message.id) >= 0) {\n message.sendState = 'pending';\n message.errorMessage = null;\n }\n });\n return newState;\n };\n\n /**\n * Set the state of message to sent.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to delete.\n * @return {Object} New state with array of pending delete message ids.\n */\n var setMessagesSendSuccessById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.messages.forEach(function(message) {\n if (messageIds.indexOf(message.id) >= 0) {\n message.sendState = 'sent';\n message.errorMessage = null;\n }\n });\n return newState;\n };\n\n /**\n * Set the state of messages to error.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to delete.\n * @param {string} errorMessage\n * @return {Object} New state with array of pending delete message ids.\n */\n var setMessagesSendFailById = function(state, messageIds, errorMessage) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.messages.forEach(function(message) {\n if (messageIds.indexOf(message.id) >= 0) {\n message.sendState = 'error';\n message.errorMessage = errorMessage;\n }\n });\n return newState;\n };\n\n /**\n * Set the visibility of the emoji picker.\n *\n * @param {Object} state Current state.\n * @param {Bool} show Should the emoji picker be shown.\n * @return {Object} New state with array of pending delete message ids.\n */\n var setShowEmojiPicker = function(state, show) {\n var newState = cloneState(state);\n newState.showEmojiPicker = show;\n return newState;\n };\n\n /**\n * Set whether emojis auto complete suggestions should be shown.\n *\n * @param {Object} state Current state.\n * @param {Bool} show Show the autocomplete\n * @return {Object} New state with array of pending delete message ids.\n */\n var setShowEmojiAutoComplete = function(state, show) {\n var newState = cloneState(state);\n newState.showEmojiAutoComplete = show;\n return newState;\n };\n\n /**\n * Set the state pending block userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to block.\n * @return {Object} New state with array of pending block userids.\n */\n var addPendingBlockUsersById = function(state, userIds) {\n var newState = cloneState(state);\n userIds.forEach(function(id) {\n newState.pendingBlockUserIds.push(id);\n });\n return newState;\n };\n\n /**\n * Set the state pending remove userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove.\n * @return {Object} New state with array of pending remove userids.\n */\n var addPendingRemoveContactsById = function(state, userIds) {\n var newState = cloneState(state);\n userIds.forEach(function(id) {\n newState.pendingRemoveContactIds.push(id);\n });\n return newState;\n };\n\n /**\n * Set the state pending unblock userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to unblock.\n * @return {Object} New state with array of pending unblock userids.\n */\n var addPendingUnblockUsersById = function(state, userIds) {\n var newState = cloneState(state);\n userIds.forEach(function(id) {\n newState.pendingUnblockUserIds.push(id);\n });\n return newState;\n };\n\n /**\n * Set the state pending add users to contacts userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to add users to contacts.\n * @return {Object} New state with array of pending add users to contacts userids.\n */\n var addPendingAddContactsById = function(state, userIds) {\n var newState = cloneState(state);\n userIds.forEach(function(id) {\n newState.pendingAddContactIds.push(id);\n });\n return newState;\n };\n\n /**\n * Set the state pending delete messages.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to delete.\n * @return {Object} New state with array of pending delete message ids.\n */\n var addPendingDeleteMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds.forEach(function(id) {\n newState.pendingDeleteMessageIds.push(id);\n });\n return newState;\n };\n\n /**\n * Update the state pending block userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove from the list of user ids to block.\n * @return {Object} New state with array of pending block userids.\n */\n var removePendingBlockUsersById = function(state, userIds) {\n var newState = cloneState(state);\n newState.pendingBlockUserIds = newState.pendingBlockUserIds.filter(function(id) {\n return userIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Update the state pending remove userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove from the list of user ids to remove.\n * @return {Object} New state with array of pending remove userids.\n */\n var removePendingRemoveContactsById = function(state, userIds) {\n var newState = cloneState(state);\n newState.pendingRemoveContactIds = newState.pendingRemoveContactIds.filter(function(id) {\n return userIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Update the state pending unblock userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove from the list of user ids to unblock.\n * @return {Object} New state with array of pending unblock userids.\n */\n var removePendingUnblockUsersById = function(state, userIds) {\n var newState = cloneState(state);\n newState.pendingUnblockUserIds = newState.pendingUnblockUserIds.filter(function(id) {\n return userIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Update the state pending add to contacts userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove from the list of user ids to add to contacts.\n * @return {Object} New state with array of pending add to contacts userids.\n */\n var removePendingAddContactsById = function(state, userIds) {\n var newState = cloneState(state);\n newState.pendingAddContactIds = newState.pendingAddContactIds.filter(function(id) {\n return userIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Update the state pending delete messages userids.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Message ids to remove from the list of messages to delete.\n * @return {Object} New state with array of messages to delete.\n */\n var removePendingDeleteMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.pendingDeleteMessageIds = newState.pendingDeleteMessageIds.filter(function(id) {\n return messageIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Add messages to state selected messages.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages that are selected.\n * @return {Object} New state with array of not blocked members.\n */\n var addSelectedMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.selectedMessageIds = newState.selectedMessageIds.concat(messageIds);\n return newState;\n };\n\n /**\n * Remove messages from the state selected messages.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to remove from selected messages.\n * @return {Object} New state with array of selected messages.\n */\n var removeSelectedMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.selectedMessageIds = newState.selectedMessageIds.filter(function(id) {\n return messageIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Mark messages as read.\n *\n * @param {Object} state Current state.\n * @param {Array} readMessages Messages that are read.\n * @return {Object} New state with array of messages that have the isread attribute set.\n */\n var markMessagesAsRead = function(state, readMessages) {\n var newState = cloneState(state);\n var readMessageIds = readMessages.map(function(message) {\n return message.id;\n });\n newState.messages = newState.messages.map(function(message) {\n if (readMessageIds.indexOf(message.id) >= 0) {\n message.isRead = true;\n }\n\n return message;\n });\n return newState;\n };\n\n /**\n * Add a contact request to each of the members that the request is for.\n *\n * @param {Object} state Current state.\n * @param {Array} requests The contact requests\n * @return {Object} New state\n */\n var addContactRequests = function(state, requests) {\n var newState = cloneState(state);\n\n requests.forEach(function(request) {\n var fromUserId = request.userid;\n var toUserId = request.requesteduserid;\n newState.members[fromUserId].contactrequests.push(request);\n newState.members[toUserId].contactrequests.push(request);\n });\n\n return newState;\n };\n\n /**\n * Remove a contact request from the members of that request.\n *\n * @param {Object} state Current state.\n * @param {Array} requests The contact requests\n * @return {Object} New state\n */\n var removeContactRequests = function(state, requests) {\n var newState = cloneState(state);\n requests.forEach(function(request) {\n var fromUserId = request.userid;\n var toUserId = request.requesteduserid;\n\n newState.members[fromUserId].contactrequests = newState.members[fromUserId].contactrequests.filter(function(existing) {\n return existing.userid != fromUserId;\n });\n newState.members[toUserId].contactrequests = newState.members[toUserId].contactrequests.filter(function(existing) {\n return existing.requesteduserid != toUserId;\n });\n });\n\n return newState;\n };\n\n /**\n * Set wheter the message of the conversation can delete for all users.\n *\n * @param {Object} state Current state.\n * @param {Bool} value If it can delete for all users.\n * @return {Object} New state.\n */\n var setCanDeleteMessagesForAllUsers = function(state, value) {\n var newState = cloneState(state);\n newState.canDeleteMessagesForAllUsers = value;\n return newState;\n };\n\n /**\n * Set wheter the messages of the conversation delete for all users.\n *\n * @param {Object} state Current state.\n * @param {Bool} value Delete messages for all users.\n * @return {Object} New state.\n */\n var setDeleteMessagesForAllUsers = function(state, value) {\n var newState = cloneState(state);\n newState.deleteMessagesForAllUsers = value;\n return newState;\n };\n\n return {\n buildInitialState: buildInitialState,\n addMessages: addMessages,\n updateMessages: updateMessages,\n removeMessages: removeMessages,\n removeMessagesById: removeMessagesById,\n addMembers: addMembers,\n removeMembers: removeMembers,\n setLoadingMessages: setLoadingMessages,\n setLoadingMembers: setLoadingMembers,\n setId: setId,\n setName: setName,\n setSubname: setSubname,\n setType: setType,\n setIsFavourite: setIsFavourite,\n setIsMuted: setIsMuted,\n setCanDeleteMessagesForAllUsers: setCanDeleteMessagesForAllUsers,\n setDeleteMessagesForAllUsers: setDeleteMessagesForAllUsers,\n setTotalMemberCount: setTotalMemberCount,\n setImageUrl: setImageUrl,\n setLoadingConfirmAction: setLoadingConfirmAction,\n setPendingDeleteConversation: setPendingDeleteConversation,\n setMessagesSendPendingById: setMessagesSendPendingById,\n setMessagesSendSuccessById: setMessagesSendSuccessById,\n setMessagesSendFailById: setMessagesSendFailById,\n setShowEmojiAutoComplete: setShowEmojiAutoComplete,\n setShowEmojiPicker: setShowEmojiPicker,\n addPendingBlockUsersById: addPendingBlockUsersById,\n addPendingRemoveContactsById: addPendingRemoveContactsById,\n addPendingUnblockUsersById: addPendingUnblockUsersById,\n addPendingAddContactsById: addPendingAddContactsById,\n addPendingDeleteMessagesById: addPendingDeleteMessagesById,\n removePendingBlockUsersById: removePendingBlockUsersById,\n removePendingRemoveContactsById: removePendingRemoveContactsById,\n removePendingUnblockUsersById: removePendingUnblockUsersById,\n removePendingAddContactsById: removePendingAddContactsById,\n removePendingDeleteMessagesById: removePendingDeleteMessagesById,\n addSelectedMessagesById: addSelectedMessagesById,\n removeSelectedMessagesById: removeSelectedMessagesById,\n markMessagesAsRead: markMessagesAsRead,\n addContactRequests: addContactRequests,\n removeContactRequests: removeContactRequests\n };\n});\n"],"names":["define","$","cloneState","state","extend","formatMessages","messages","loggedInUserId","members","map","message","fromLoggedInUser","useridfrom","id","userFrom","text","timeCreated","timecreated","parseInt","buildInitialState","midnight","messagePollMin","messagePollMax","messagePollAfterMax","name","subname","type","totalMemberCount","imageUrl","isFavourite","isMuted","canDeleteMessagesForAllUsers","deleteMessagesForAllUsers","hasTriedToLoadMessages","loadingMessages","loadingMembers","loadingConfirmAction","pendingBlockUserIds","pendingUnblockUserIds","pendingRemoveContactIds","pendingAddContactIds","pendingDeleteMessageIds","pendingSendMessageIds","pendingDeleteConversation","selectedMessageIds","showEmojiAutoComplete","showEmojiPicker","addMessages","newState","formattedMessages","sendState","timeAdded","Date","now","errorMessage","allMessages","concat","sort","a","b","filter","index","sortedMessages","updateMessages","data","updatesById","reduce","carry","messageData","oldMessage","newMessage","formattedMessage","removeMessages","removeMessageIds","indexOf","removeMessagesById","messageIds","addMembers","formattedMembers","member","fullname","profileurl","profileimageurl","profileimageurlsmall","isonline","showonlinestatus","isblocked","iscontact","isdeleted","canmessage","canmessageevenifblocked","requirescontact","contactrequests","cancreatecontact","formatMembers","forEach","removeMembers","setLoadingMessages","value","setLoadingMembers","setId","setName","setSubname","setType","setIsFavourite","setIsMuted","setCanDeleteMessagesForAllUsers","setDeleteMessagesForAllUsers","setTotalMemberCount","count","setImageUrl","url","setLoadingConfirmAction","setPendingDeleteConversation","setMessagesSendPendingById","setMessagesSendSuccessById","setMessagesSendFailById","setShowEmojiAutoComplete","show","setShowEmojiPicker","addPendingBlockUsersById","userIds","push","addPendingRemoveContactsById","addPendingUnblockUsersById","addPendingAddContactsById","addPendingDeleteMessagesById","removePendingBlockUsersById","removePendingRemoveContactsById","removePendingUnblockUsersById","removePendingAddContactsById","removePendingDeleteMessagesById","addSelectedMessagesById","removeSelectedMessagesById","markMessagesAsRead","readMessages","readMessageIds","isRead","addContactRequests","requests","request","fromUserId","userid","toUserId","requesteduserid","removeContactRequests","existing"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"message_drawer_view_conversation_state_manager.min.js","sources":["../src/message_drawer_view_conversation_state_manager.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 .\n\n/**\n * This module operates on the view states from the message_drawer_view_conversation module.\n * It exposes functions that can be used to generate new version of the state.\n *\n * Important notes for this module:\n * 1.) The existing state is always immutable. It should never be modified.\n * 2.) All functions that operate on the state should always clone the state and\n * modify the cloned state before returning it.\n *\n * It's important that the states remain immutable because they are diff'd in\n * the message_drawer_view_conversation_patcher module in order to work out what\n * has changed.\n *\n * @module core_message/message_drawer_view_conversation_state_manager\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n /**\n * Clone a state, a state is a collection of information about the variables required to build\n * the conversation user interface.\n *\n * @param {Object} state State to clone\n * @return {Object} newstate A copy of the state to clone.\n */\n var cloneState = function(state) {\n // Do a deep extend to make sure we recursively copy objects and\n // arrays so that the new state doesn't contain any references to\n // the old state, e.g. adding a value to an array in the new state\n // shouldn't also add it to the old state.\n return $.extend(true, {}, state);\n };\n\n /**\n * Format messages to be used in a state.\n *\n * @param {Array} messages The messages to format.\n * @param {Number} loggedInUserId The logged in user id.\n * @param {Array} members The converstation members.\n * @return {Array} Formatted messages.\n */\n var formatMessages = function(messages, loggedInUserId, members) {\n return messages.map(function(message) {\n var fromLoggedInUser = message.useridfrom == loggedInUserId;\n return {\n // Stringify the id.\n id: \"\" + message.id,\n fromLoggedInUser: fromLoggedInUser,\n userFrom: members[message.useridfrom],\n text: message.text,\n timeCreated: message.timecreated ? parseInt(message.timecreated, 10) : null\n };\n });\n };\n\n /**\n * Format members to be used in a state.\n *\n * @param {Array} members The messages to format.\n * @return {Array} Formatted members.\n */\n var formatMembers = function(members) {\n return members.map(function(member) {\n return {\n id: member.id,\n fullname: member.fullname,\n profileurl: member.profileurl,\n profileimageurl: member.profileimageurl,\n profileimageurlsmall: member.profileimageurlsmall,\n isonline: member.isonline,\n showonlinestatus: member.showonlinestatus,\n isblocked: member.isblocked,\n iscontact: member.iscontact,\n isdeleted: member.isdeleted,\n canmessage: member.canmessage,\n canmessageevenifblocked: member.canmessageevenifblocked,\n requirescontact: member.requirescontact,\n contactrequests: member.contactrequests || [],\n cancreatecontact: member.cancreatecontact,\n };\n });\n };\n\n /**\n * Create an initial (blank) state.\n *\n * @param {Number} midnight Midnight time.\n * @param {Number} loggedInUserId The logged in user id.\n * @param {Number} id The conversation id.\n * @param {Number} messagePollMin The message poll start timeout in seconds.\n * @param {Number} messagePollMax The message poll max timeout limit in seconds.\n * @param {Number} messagePollAfterMax The message poll frequency in seconds to reset to after max limit is reached.\n * @return {Object} Initial state.\n */\n var buildInitialState = function(\n midnight,\n loggedInUserId,\n id,\n messagePollMin,\n messagePollMax,\n messagePollAfterMax\n ) {\n return {\n midnight: midnight,\n loggedInUserId: loggedInUserId,\n id: id,\n messagePollMin: messagePollMin,\n messagePollMax: messagePollMax,\n messagePollAfterMax: messagePollAfterMax,\n name: null,\n subname: null,\n type: null,\n totalMemberCount: null,\n imageUrl: null,\n isFavourite: null,\n isMuted: null,\n canDeleteMessagesForAllUsers: false,\n deleteMessagesForAllUsers: false,\n members: {},\n messages: [],\n hasTriedToLoadMessages: false,\n loadingMessages: true,\n loadingMembers: true,\n loadingConfirmAction: false,\n pendingBlockUserIds: [],\n pendingUnblockUserIds: [],\n pendingRemoveContactIds: [],\n pendingAddContactIds: [],\n pendingDeleteMessageIds: [],\n pendingSendMessageIds: [],\n pendingDeleteConversation: false,\n selectedMessageIds: [],\n showEmojiAutoComplete: false,\n showEmojiPicker: false,\n canSendMessageToConversation: true,\n };\n };\n\n /**\n * Add messages to a state and sort them by timecreated.\n *\n * @param {Object} state Current state.\n * @param {Array} messages Messages to add to state.\n * @return {Object} state New state with added messages.\n */\n var addMessages = function(state, messages) {\n var newState = cloneState(state);\n var formattedMessages = formatMessages(messages, state.loggedInUserId, state.members);\n formattedMessages = formattedMessages.map(function(message) {\n message.sendState = null;\n message.timeAdded = Date.now();\n message.errorMessage = null;\n return message;\n });\n var allMessages = state.messages.concat(formattedMessages);\n // Sort the messages. Oldest to newest.\n allMessages.sort(function(a, b) {\n if (a.timeCreated === null && b.timeCreated === null) {\n if (a.timeAdded < b.timeAdded) {\n return -1;\n } else if (a.timeAdded > b.timeAdded) {\n return 1;\n }\n }\n\n if (a.timeCreated === null && b.timeCreated !== null) {\n // A comes after b.\n return 1;\n } else if (a.timeCreated !== null && b.timeCreated === null) {\n // A comes before b.\n return -1;\n } else if (a.timeCreated < b.timeCreated) {\n // A comes before b.\n return -1;\n } else if (a.timeCreated > b.timeCreated) {\n // A comes after b.\n return 1;\n } else if (a.id < b.id) {\n return -1;\n } else if (a.id > b.id) {\n return 1;\n } else {\n return 0;\n }\n });\n\n // Filter out any duplicate messages.\n newState.messages = allMessages.filter(function(message, index, sortedMessages) {\n return !index || message.id != sortedMessages[index - 1].id;\n });\n\n return newState;\n };\n\n /**\n * Update existing messages.\n *\n * @param {Object} state Current state.\n * @param {Array} data 2D array of old and new messages\n * @return {Object} state.\n */\n var updateMessages = function(state, data) {\n var newState = cloneState(state);\n var updatesById = data.reduce(function(carry, messageData) {\n var oldMessage = messageData[0];\n var newMessage = messageData[1];\n var formattedMessages = formatMessages([newMessage], state.loggedInUserId, state.members);\n var formattedMessage = formattedMessages[0];\n\n carry[oldMessage.id] = formattedMessage;\n return carry;\n }, {});\n\n newState.messages = newState.messages.map(function(message) {\n if (message.id in updatesById) {\n return $.extend(message, updatesById[message.id]);\n } else {\n return message;\n }\n });\n\n return newState;\n };\n\n /**\n * Remove messages from state.\n *\n * @param {Object} state Current state.\n * @param {Array} messages Messages to remove from state.\n * @return {Object} state New state with removed messages.\n */\n var removeMessages = function(state, messages) {\n var newState = cloneState(state);\n var removeMessageIds = messages.map(function(message) {\n return \"\" + message.id;\n });\n newState.messages = newState.messages.filter(function(message) {\n return removeMessageIds.indexOf(message.id) < 0;\n });\n\n return newState;\n };\n\n /**\n * Remove messages from state by message id.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Message ids to remove from state.\n * @return {Object} state New state with removed messages.\n */\n var removeMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.messages = newState.messages.filter(function(message) {\n return messageIds.indexOf(message.id) < 0;\n });\n\n return newState;\n };\n\n /**\n * Add conversation member to state.\n *\n * @param {Object} state Current state.\n * @param {Array} members Conversation members to be added to state.\n * @return {Object} New state with added members.\n */\n var addMembers = function(state, members) {\n var newState = cloneState(state);\n var formattedMembers = formatMembers(members);\n formattedMembers.forEach(function(member) {\n newState.members[member.id] = member;\n });\n return newState;\n };\n\n /**\n * Remove members from state.\n *\n * @param {Object} state Current state.\n * @param {Array} members Members to be removed from state.\n * @return {Object} New state with removed members.\n */\n var removeMembers = function(state, members) {\n var newState = cloneState(state);\n members.forEach(function(member) {\n delete newState.members[member.id];\n });\n return newState;\n };\n\n /**\n * Set the state loading messages attribute.\n *\n * @param {Object} state Current state.\n * @param {Bool} value New loading messages value.\n * @return {Object} New state with loading messages attribute.\n */\n var setLoadingMessages = function(state, value) {\n var newState = cloneState(state);\n newState.loadingMessages = value;\n if (state.loadingMessages && !value) {\n // If we're going from loading to not loading then\n // it means we've tried to load.\n newState.hasTriedToLoadMessages = true;\n }\n return newState;\n };\n\n /**\n * Set the state loading members attribute.\n *\n * @param {Object} state Current state.\n * @param {Bool} value New loading members value.\n * @return {Object} New state with loading members attribute.\n */\n var setLoadingMembers = function(state, value) {\n var newState = cloneState(state);\n newState.loadingMembers = value;\n return newState;\n };\n\n /**\n * Set the conversation id.\n *\n * @param {Object} state Current state.\n * @param {String} value The ID.\n * @return {Object} New state.\n */\n var setId = function(state, value) {\n var newState = cloneState(state);\n newState.id = value;\n return newState;\n };\n\n /**\n * Set the state name attribute.\n *\n * @param {Object} state Current state.\n * @param {String} value New name value.\n * @return {Object} New state with name attribute.\n */\n var setName = function(state, value) {\n var newState = cloneState(state);\n newState.name = value;\n return newState;\n };\n\n /**\n * Set the state subname attribute.\n *\n * @param {Object} state Current state.\n * @param {String} value New subname value.\n * @return {Object} New state.\n */\n var setSubname = function(state, value) {\n var newState = cloneState(state);\n newState.subname = value;\n return newState;\n };\n\n /**\n * Set the conversation type.\n *\n * @param {Object} state Current state.\n * @param {Int} type Conversation type.\n * @return {Object} New state.\n */\n var setType = function(state, type) {\n var newState = cloneState(state);\n newState.type = type;\n return newState;\n };\n\n /**\n * Set whether the conversation is a favourite conversation.\n *\n * @param {Object} state Current state.\n * @param {Bool} isFavourite If it's a favourite.\n * @return {Object} New state.\n */\n var setIsFavourite = function(state, isFavourite) {\n var newState = cloneState(state);\n newState.isFavourite = isFavourite;\n return newState;\n };\n\n /**\n * Set whether the conversation is a muted conversation.\n *\n * @param {Object} state Current state.\n * @param {bool} isMuted If it's muted.\n * @return {Object} New state.\n */\n var setIsMuted = function(state, isMuted) {\n var newState = cloneState(state);\n newState.isMuted = isMuted;\n return newState;\n };\n\n /**\n * Set the total member count.\n *\n * @param {Object} state Current state.\n * @param {String} count The count.\n * @return {Object} New state.\n */\n var setTotalMemberCount = function(state, count) {\n var newState = cloneState(state);\n newState.totalMemberCount = count;\n return newState;\n };\n\n /**\n * Set the conversation image url.\n *\n * @param {Object} state Current state.\n * @param {String} url The url to the image.\n * @return {Object} New state.\n */\n var setImageUrl = function(state, url) {\n var newState = cloneState(state);\n newState.imageUrl = url;\n return newState;\n };\n\n /**\n * Set the state loading confirm action attribute.\n *\n * @param {Object} state Current state.\n * @param {Bool} value New loading confirm action value.\n * @return {Object} New state with loading confirm action attribute.\n */\n var setLoadingConfirmAction = function(state, value) {\n var newState = cloneState(state);\n newState.loadingConfirmAction = value;\n return newState;\n };\n\n /**\n * Set the state pending delete conversation attribute.\n *\n * @param {Object} state Current state.\n * @param {Bool} value New pending delete conversation value.\n * @return {Object} New state with pending delete conversation attribute.\n */\n var setPendingDeleteConversation = function(state, value) {\n var newState = cloneState(state);\n newState.pendingDeleteConversation = value;\n return newState;\n };\n\n /**\n * Set the state of message to pending.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to delete.\n * @return {Object} New state with array of pending delete message ids.\n */\n var setMessagesSendPendingById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.messages.forEach(function(message) {\n if (messageIds.indexOf(message.id) >= 0) {\n message.sendState = 'pending';\n message.errorMessage = null;\n }\n });\n return newState;\n };\n\n /**\n * Set the state of message to sent.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to delete.\n * @return {Object} New state with array of pending delete message ids.\n */\n var setMessagesSendSuccessById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.messages.forEach(function(message) {\n if (messageIds.indexOf(message.id) >= 0) {\n message.sendState = 'sent';\n message.errorMessage = null;\n }\n });\n return newState;\n };\n\n /**\n * Set the state of messages to error.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to delete.\n * @param {string} errorMessage\n * @return {Object} New state with array of pending delete message ids.\n */\n var setMessagesSendFailById = function(state, messageIds, errorMessage) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.messages.forEach(function(message) {\n if (messageIds.indexOf(message.id) >= 0) {\n message.sendState = 'error';\n message.errorMessage = errorMessage;\n }\n });\n return newState;\n };\n\n /**\n * Set the visibility of the emoji picker.\n *\n * @param {Object} state Current state.\n * @param {Bool} show Should the emoji picker be shown.\n * @return {Object} New state with array of pending delete message ids.\n */\n var setShowEmojiPicker = function(state, show) {\n var newState = cloneState(state);\n newState.showEmojiPicker = show;\n return newState;\n };\n\n /**\n * Set whether emojis auto complete suggestions should be shown.\n *\n * @param {Object} state Current state.\n * @param {Bool} show Show the autocomplete\n * @return {Object} New state with array of pending delete message ids.\n */\n var setShowEmojiAutoComplete = function(state, show) {\n var newState = cloneState(state);\n newState.showEmojiAutoComplete = show;\n return newState;\n };\n\n /**\n * Set the state pending block userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to block.\n * @return {Object} New state with array of pending block userids.\n */\n var addPendingBlockUsersById = function(state, userIds) {\n var newState = cloneState(state);\n userIds.forEach(function(id) {\n newState.pendingBlockUserIds.push(id);\n });\n return newState;\n };\n\n /**\n * Set the state pending remove userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove.\n * @return {Object} New state with array of pending remove userids.\n */\n var addPendingRemoveContactsById = function(state, userIds) {\n var newState = cloneState(state);\n userIds.forEach(function(id) {\n newState.pendingRemoveContactIds.push(id);\n });\n return newState;\n };\n\n /**\n * Set the state pending unblock userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to unblock.\n * @return {Object} New state with array of pending unblock userids.\n */\n var addPendingUnblockUsersById = function(state, userIds) {\n var newState = cloneState(state);\n userIds.forEach(function(id) {\n newState.pendingUnblockUserIds.push(id);\n });\n return newState;\n };\n\n /**\n * Set the state pending add users to contacts userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to add users to contacts.\n * @return {Object} New state with array of pending add users to contacts userids.\n */\n var addPendingAddContactsById = function(state, userIds) {\n var newState = cloneState(state);\n userIds.forEach(function(id) {\n newState.pendingAddContactIds.push(id);\n });\n return newState;\n };\n\n /**\n * Set the state pending delete messages.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to delete.\n * @return {Object} New state with array of pending delete message ids.\n */\n var addPendingDeleteMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds.forEach(function(id) {\n newState.pendingDeleteMessageIds.push(id);\n });\n return newState;\n };\n\n /**\n * Update the state pending block userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove from the list of user ids to block.\n * @return {Object} New state with array of pending block userids.\n */\n var removePendingBlockUsersById = function(state, userIds) {\n var newState = cloneState(state);\n newState.pendingBlockUserIds = newState.pendingBlockUserIds.filter(function(id) {\n return userIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Update the state pending remove userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove from the list of user ids to remove.\n * @return {Object} New state with array of pending remove userids.\n */\n var removePendingRemoveContactsById = function(state, userIds) {\n var newState = cloneState(state);\n newState.pendingRemoveContactIds = newState.pendingRemoveContactIds.filter(function(id) {\n return userIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Update the state pending unblock userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove from the list of user ids to unblock.\n * @return {Object} New state with array of pending unblock userids.\n */\n var removePendingUnblockUsersById = function(state, userIds) {\n var newState = cloneState(state);\n newState.pendingUnblockUserIds = newState.pendingUnblockUserIds.filter(function(id) {\n return userIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Update the state pending add to contacts userids.\n *\n * @param {Object} state Current state.\n * @param {Array} userIds User ids to remove from the list of user ids to add to contacts.\n * @return {Object} New state with array of pending add to contacts userids.\n */\n var removePendingAddContactsById = function(state, userIds) {\n var newState = cloneState(state);\n newState.pendingAddContactIds = newState.pendingAddContactIds.filter(function(id) {\n return userIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Update the state pending delete messages userids.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Message ids to remove from the list of messages to delete.\n * @return {Object} New state with array of messages to delete.\n */\n var removePendingDeleteMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.pendingDeleteMessageIds = newState.pendingDeleteMessageIds.filter(function(id) {\n return messageIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Add messages to state selected messages.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages that are selected.\n * @return {Object} New state with array of not blocked members.\n */\n var addSelectedMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.selectedMessageIds = newState.selectedMessageIds.concat(messageIds);\n return newState;\n };\n\n /**\n * Remove messages from the state selected messages.\n *\n * @param {Object} state Current state.\n * @param {Array} messageIds Messages to remove from selected messages.\n * @return {Object} New state with array of selected messages.\n */\n var removeSelectedMessagesById = function(state, messageIds) {\n var newState = cloneState(state);\n messageIds = messageIds.map(function(id) {\n return \"\" + id;\n });\n newState.selectedMessageIds = newState.selectedMessageIds.filter(function(id) {\n return messageIds.indexOf(id) < 0;\n });\n return newState;\n };\n\n /**\n * Mark messages as read.\n *\n * @param {Object} state Current state.\n * @param {Array} readMessages Messages that are read.\n * @return {Object} New state with array of messages that have the isread attribute set.\n */\n var markMessagesAsRead = function(state, readMessages) {\n var newState = cloneState(state);\n var readMessageIds = readMessages.map(function(message) {\n return message.id;\n });\n newState.messages = newState.messages.map(function(message) {\n if (readMessageIds.indexOf(message.id) >= 0) {\n message.isRead = true;\n }\n\n return message;\n });\n return newState;\n };\n\n /**\n * Add a contact request to each of the members that the request is for.\n *\n * @param {Object} state Current state.\n * @param {Array} requests The contact requests\n * @return {Object} New state\n */\n var addContactRequests = function(state, requests) {\n var newState = cloneState(state);\n\n requests.forEach(function(request) {\n var fromUserId = request.userid;\n var toUserId = request.requesteduserid;\n newState.members[fromUserId].contactrequests.push(request);\n newState.members[toUserId].contactrequests.push(request);\n });\n\n return newState;\n };\n\n /**\n * Remove a contact request from the members of that request.\n *\n * @param {Object} state Current state.\n * @param {Array} requests The contact requests\n * @return {Object} New state\n */\n var removeContactRequests = function(state, requests) {\n var newState = cloneState(state);\n requests.forEach(function(request) {\n var fromUserId = request.userid;\n var toUserId = request.requesteduserid;\n\n newState.members[fromUserId].contactrequests = newState.members[fromUserId].contactrequests.filter(function(existing) {\n return existing.userid != fromUserId;\n });\n newState.members[toUserId].contactrequests = newState.members[toUserId].contactrequests.filter(function(existing) {\n return existing.requesteduserid != toUserId;\n });\n });\n\n return newState;\n };\n\n /**\n * Set wheter the message of the conversation can delete for all users.\n *\n * @param {Object} state Current state.\n * @param {Bool} value If it can delete for all users.\n * @return {Object} New state.\n */\n var setCanDeleteMessagesForAllUsers = function(state, value) {\n var newState = cloneState(state);\n newState.canDeleteMessagesForAllUsers = value;\n return newState;\n };\n\n /**\n * Set wheter the messages of the conversation delete for all users.\n *\n * @param {Object} state Current state.\n * @param {Bool} value Delete messages for all users.\n * @return {Object} New state.\n */\n var setDeleteMessagesForAllUsers = function(state, value) {\n var newState = cloneState(state);\n newState.deleteMessagesForAllUsers = value;\n return newState;\n };\n\n /**\n * Set whether the user can send messages to the conversation.\n *\n * @param {Object} state Current state.\n * @param {Bool} value If it can send message to conversation.\n * @return {Object} New state.\n */\n const setCanSendMessageToConversation = function(state, value) {\n const newState = cloneState(state);\n newState.canSendMessageToConversation = value;\n return newState;\n };\n\n return {\n buildInitialState: buildInitialState,\n addMessages: addMessages,\n updateMessages: updateMessages,\n removeMessages: removeMessages,\n removeMessagesById: removeMessagesById,\n addMembers: addMembers,\n removeMembers: removeMembers,\n setLoadingMessages: setLoadingMessages,\n setLoadingMembers: setLoadingMembers,\n setId: setId,\n setName: setName,\n setSubname: setSubname,\n setType: setType,\n setIsFavourite: setIsFavourite,\n setIsMuted: setIsMuted,\n setCanDeleteMessagesForAllUsers: setCanDeleteMessagesForAllUsers,\n setDeleteMessagesForAllUsers: setDeleteMessagesForAllUsers,\n setTotalMemberCount: setTotalMemberCount,\n setImageUrl: setImageUrl,\n setLoadingConfirmAction: setLoadingConfirmAction,\n setPendingDeleteConversation: setPendingDeleteConversation,\n setMessagesSendPendingById: setMessagesSendPendingById,\n setMessagesSendSuccessById: setMessagesSendSuccessById,\n setMessagesSendFailById: setMessagesSendFailById,\n setShowEmojiAutoComplete: setShowEmojiAutoComplete,\n setShowEmojiPicker: setShowEmojiPicker,\n addPendingBlockUsersById: addPendingBlockUsersById,\n addPendingRemoveContactsById: addPendingRemoveContactsById,\n addPendingUnblockUsersById: addPendingUnblockUsersById,\n addPendingAddContactsById: addPendingAddContactsById,\n addPendingDeleteMessagesById: addPendingDeleteMessagesById,\n removePendingBlockUsersById: removePendingBlockUsersById,\n removePendingRemoveContactsById: removePendingRemoveContactsById,\n removePendingUnblockUsersById: removePendingUnblockUsersById,\n removePendingAddContactsById: removePendingAddContactsById,\n removePendingDeleteMessagesById: removePendingDeleteMessagesById,\n addSelectedMessagesById: addSelectedMessagesById,\n removeSelectedMessagesById: removeSelectedMessagesById,\n markMessagesAsRead: markMessagesAsRead,\n addContactRequests: addContactRequests,\n removeContactRequests: removeContactRequests,\n setCanSendMessageToConversation: setCanSendMessageToConversation,\n };\n});\n"],"names":["define","$","cloneState","state","extend","formatMessages","messages","loggedInUserId","members","map","message","fromLoggedInUser","useridfrom","id","userFrom","text","timeCreated","timecreated","parseInt","buildInitialState","midnight","messagePollMin","messagePollMax","messagePollAfterMax","name","subname","type","totalMemberCount","imageUrl","isFavourite","isMuted","canDeleteMessagesForAllUsers","deleteMessagesForAllUsers","hasTriedToLoadMessages","loadingMessages","loadingMembers","loadingConfirmAction","pendingBlockUserIds","pendingUnblockUserIds","pendingRemoveContactIds","pendingAddContactIds","pendingDeleteMessageIds","pendingSendMessageIds","pendingDeleteConversation","selectedMessageIds","showEmojiAutoComplete","showEmojiPicker","canSendMessageToConversation","addMessages","newState","formattedMessages","sendState","timeAdded","Date","now","errorMessage","allMessages","concat","sort","a","b","filter","index","sortedMessages","updateMessages","data","updatesById","reduce","carry","messageData","oldMessage","newMessage","formattedMessage","removeMessages","removeMessageIds","indexOf","removeMessagesById","messageIds","addMembers","formattedMembers","member","fullname","profileurl","profileimageurl","profileimageurlsmall","isonline","showonlinestatus","isblocked","iscontact","isdeleted","canmessage","canmessageevenifblocked","requirescontact","contactrequests","cancreatecontact","formatMembers","forEach","removeMembers","setLoadingMessages","value","setLoadingMembers","setId","setName","setSubname","setType","setIsFavourite","setIsMuted","setCanDeleteMessagesForAllUsers","setDeleteMessagesForAllUsers","setTotalMemberCount","count","setImageUrl","url","setLoadingConfirmAction","setPendingDeleteConversation","setMessagesSendPendingById","setMessagesSendSuccessById","setMessagesSendFailById","setShowEmojiAutoComplete","show","setShowEmojiPicker","addPendingBlockUsersById","userIds","push","addPendingRemoveContactsById","addPendingUnblockUsersById","addPendingAddContactsById","addPendingDeleteMessagesById","removePendingBlockUsersById","removePendingRemoveContactsById","removePendingUnblockUsersById","removePendingAddContactsById","removePendingDeleteMessagesById","addSelectedMessagesById","removeSelectedMessagesById","markMessagesAsRead","readMessages","readMessageIds","isRead","addContactRequests","requests","request","fromUserId","userid","toUserId","requesteduserid","removeContactRequests","existing","setCanSendMessageToConversation"],"mappings":"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"} \ No newline at end of file diff --git a/public/message/amd/src/message_drawer_view_conversation.js b/public/message/amd/src/message_drawer_view_conversation.js index dc172586deb..683fbb2b86e 100644 --- a/public/message/amd/src/message_drawer_view_conversation.js +++ b/public/message/amd/src/message_drawer_view_conversation.js @@ -362,6 +362,7 @@ function( newState = StateManager.setIsMuted(newState, conversation.ismuted); newState = StateManager.addMessages(newState, conversation.messages); newState = StateManager.setCanDeleteMessagesForAllUsers(newState, conversation.candeletemessagesforallusers); + newState = StateManager.setCanSendMessageToConversation(newState, conversation.cansendmessagetoconversation); return newState; }; diff --git a/public/message/amd/src/message_drawer_view_conversation_constants.js b/public/message/amd/src/message_drawer_view_conversation_constants.js index d1023f8cdba..580b266fce1 100644 --- a/public/message/amd/src/message_drawer_view_conversation_constants.js +++ b/public/message/amd/src/message_drawer_view_conversation_constants.js @@ -62,7 +62,6 @@ define([], function() { CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER: '[data-region="content-messages-footer-edit-mode-container"]', CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER: '[data-region="content-messages-footer-require-contact-container"]', CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER: '[data-region="content-messages-footer-require-unblock-container"]', - CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER: '[data-region="content-messages-footer-unable-to-message"]', 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"]', @@ -97,7 +96,9 @@ define([], function() { TEXT_CONTAINER: '[data-region="text-container"]', TIME_CREATED: '[data-region="time-created"]', TITLE: '[data-region="title"]', - TOGGLE_EMOJI_PICKER_BUTTON: '[data-action="toggle-emoji-picker"]' + TOGGLE_EMOJI_PICKER_BUTTON: '[data-action="toggle-emoji-picker"]', + DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER: '[data-region="day-message-unable-to-send-container"]', + UNABLE_TO_MESSAGE_CONTAINER: '[data-region="unable-to-send-container"]', }; var TEMPLATES = { diff --git a/public/message/amd/src/message_drawer_view_conversation_patcher.js b/public/message/amd/src/message_drawer_view_conversation_patcher.js index 3341a25c9e6..02a482ac256 100644 --- a/public/message/amd/src/message_drawer_view_conversation_patcher.js +++ b/public/message/amd/src/message_drawer_view_conversation_patcher.js @@ -374,7 +374,8 @@ function( // 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) + messages: buildMessagesPatch(daysDiff.matches), + unableToMessage: buildUnableToMessagePatch(state, newState), }; } else { return null; @@ -1125,30 +1126,30 @@ function( * * @param {Object} state The current state. * @param {Object} newState The new state. - * @return {Bool|Null} + * @return {Bool} */ - var buildUnableToMessage = function(state, newState) { - var oldOtherUser = getOtherUserFromState(state); - var newOtherUser = getOtherUserFromState(newState); - + const buildUnableToMessagePatch = (state, newState) => { if (newState.type == Constants.CONVERSATION_TYPES.SELF) { // Users always can send message themselves on self-conversations. - return null; + return false; } + const oldOtherUser = getOtherUserFromState(state); + const newOtherUser = getOtherUserFromState(newState); + if (!oldOtherUser && !newOtherUser) { - return null; + return false; } else if (oldOtherUser && !newOtherUser) { - return oldOtherUser.canmessage ? null : true; + return !oldOtherUser.canmessage; } else if (!oldOtherUser && newOtherUser) { - return newOtherUser.canmessage ? null : true; + return !newOtherUser.canmessage; } else if (!oldOtherUser.canmessage && newOtherUser.canmessage) { return false; } else if (oldOtherUser.canmessage && !newOtherUser.canmessage) { return true; } - return null; + return !newState.canSendMessageToConversation; }; /** @@ -1163,7 +1164,6 @@ function( var inEditMode = buildInEditMode(state, newState); var requireAddContact = buildRequireAddContact(state, newState); var requireUnblock = buildRequireUnblock(state, newState); - var unableToMessage = buildUnableToMessage(state, newState); var showRequireAddContact = requireAddContact !== null ? requireAddContact.show && requireAddContact.hasMessages : null; var otherUser = getOtherUserFromState(newState); var generateReturnValue = function(checkValue, successReturn) { @@ -1179,8 +1179,6 @@ function( type: 'add-contact', user: otherUser }; - } else if (!otherUser.canmessage && (otherUser.requirescontact && !otherUser.iscontact)) { - return {type: 'unable-to-message'}; } } @@ -1199,7 +1197,6 @@ function( var checks = [ [loadingFirstMessages, {type: 'placeholder'}], [inEditMode, {type: 'edit-mode'}], - [unableToMessage, {type: 'unable-to-message'}], [requireUnblock, {type: 'unblock'}], [showRequireAddContact, {type: 'add-contact', user: otherUser}] ]; @@ -1365,7 +1362,8 @@ function( isFavourite: buildIsFavourite, isMuted: buildIsMuted, showEmojiPicker: buildShowEmojiPicker, - showEmojiAutoComplete: buildShowEmojiAutoComplete + showEmojiAutoComplete: buildShowEmojiAutoComplete, + unableToMessage: buildUnableToMessagePatch } }; // These build functions are only applicable to private conversations. diff --git a/public/message/amd/src/message_drawer_view_conversation_renderer.js b/public/message/amd/src/message_drawer_view_conversation_renderer.js index c4f3aa1f0af..ac0a1f4053e 100644 --- a/public/message/amd/src/message_drawer_view_conversation_renderer.js +++ b/public/message/amd/src/message_drawer_view_conversation_renderer.js @@ -89,6 +89,46 @@ function( return body.find(SELECTORS.SELF_CONVERSATION_MESSAGE_CONTAINER); }; + /** + * Get the unable to message container element. + * + * @param {Object} body Conversation body container element. + * @return {Object} The unable to message container element. + */ + const getUnableToMessageContainer = (body) => { + return body.find(SELECTORS.UNABLE_TO_MESSAGE_CONTAINER); + }; + + /** + * Get the message text area element. + * + * @param {Object} footer Conversation footer container element. + * @return {Object} The message text area element. + */ + const getMessageTextArea = (footer) => { + return footer.find(SELECTORS.MESSAGE_TEXT_AREA); + }; + + /** + * Get the send message button element. + * + * @param {Object} footer Conversation footer container element. + * @return {Object} The send message button element. + */ + const getSendMessageButton = (footer) => { + return footer.find(SELECTORS.SEND_MESSAGE_BUTTON); + }; + + /** + * Get the emoji picker button element. + * + * @param {Object} footer Conversation footer container element. + * @return {Object} The emoji picker button element. + */ + const getEmojiPickerButton = (footer) => { + return footer.find(SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON); + }; + /** * Hide the self-conversation message container element. * @@ -259,33 +299,6 @@ function( getFooterRequireUnblockContainer(footer).addClass('hidden'); }; - /** - * Get the footer Unable to message contact container element. - * - * @param {Object} footer Conversation footer container element. - * @return {Object} The footer Unable to message contact container element. - */ - var getFooterUnableToMessageContainer = function(footer) { - return footer.find(SELECTORS.CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER); - }; - - /** - * Show the footer Unable to message contact container element. - * - * @param {Object} footer Conversation footer container element. - */ - var showFooterUnableToMessage = function(footer) { - getFooterUnableToMessageContainer(footer).removeClass('hidden'); - }; - - /** - * Hide the footer Unable to message contact container element. - * - * @param {Object} footer Conversation footer container element. - */ - var hideFooterUnableToMessage = function(footer) { - getFooterUnableToMessageContainer(footer).addClass('hidden'); - }; /** * Hide all header elements. @@ -309,7 +322,6 @@ function( hideFooterPlaceholder(footer); hideFooterRequireContact(footer); hideFooterRequireUnblock(footer); - hideFooterUnableToMessage(footer); }; /** @@ -368,6 +380,111 @@ function( getHeaderContent(header).addClass('hidden'); }; + /** + * Show the unable to message container. + * + * @param {Object} body Conversation body container element. + */ + const showUnableToMessage = (body) => { + getUnableToMessageContainer(body).removeClass('hidden'); + }; + + /** + * Hide the unable to message container. + * + * @param {Object} body Conversation body container element. + */ + const hideUnableToMessage = (body) => { + getUnableToMessageContainer(body).addClass('hidden'); + }; + + /** + * Enable the emoji picker button. + * + * @param {Object} footer Conversation footer container element. + */ + const enableEmojiPickerButton = (footer) => { + getEmojiPickerButton(footer).prop('disabled', false); + }; + + /** + * Enable the send message button. + * + * @param {Object} footer Conversation footer container element. + */ + const enableSendMessageButton = (footer) => { + getSendMessageButton(footer).prop('disabled', false); + }; + + /** + * Enable the message text area. + * + * @param {Object} footer Conversation footer container element. + */ + const enableMessageTextArea = (footer) => { + const messageTextArea = getMessageTextArea(footer); + messageTextArea.prop('disabled', false); + Aria.unhide(messageTextArea.get()); + + Str.get_string('writeamessage', 'core_message').done(function(string) { + if (!messageTextArea.prop('disabled')) { + messageTextArea.prop('placeholder', string); + } + }); + }; + + /** + * Enable all footer content elements. + * + * @param {Object} footer Conversation footer container element. + */ + const enableAllFooterContentElements = (footer) => { + enableEmojiPickerButton(footer); + enableSendMessageButton(footer); + enableMessageTextArea(footer); + }; + + /** + * Disable the emoji picker button. + * + * @param {Object} footer Conversation footer container element. + */ + const disableEmojiPickerButton = (footer) => { + getEmojiPickerButton(footer).prop('disabled', true); + }; + + /** + * Disable the send message button. + * + * @param {Object} footer Conversation footer container element. + */ + const disableSendMessageButton = (footer) => { + getSendMessageButton(footer).prop('disabled', true); + }; + + /** + * Disable the message text area. + * + * @param {Object} footer Conversation footer container element. + */ + const disableMessageTextArea = (footer) => { + const messageTextArea = getMessageTextArea(footer); + messageTextArea.prop('disabled', true); + messageTextArea.removeAttr('placeholder'); + Aria.unhide(messageTextArea.get()); + }; + + /** + * Disable all footer content elements. + * + * @param {Object} footer Conversation footer container element. + */ + const disableAllFooterContentElements = (footer) => { + disableEmojiPickerButton(footer); + disableSendMessageButton(footer); + disableMessageTextArea(footer); + }; + /** * Get the header edit mode container element. * @@ -572,9 +689,10 @@ function( * @param {Object} footer The footer container element. * @param {Array} days Array of days containing messages. * @param {Object} datesCache Cache timestamps and their formatted date string. + * @param {Boolean} isAbleToMessage Whether the user can send a message to the conversation. * @return {Promise} Days rendering promises. */ - var renderAddDays = function(header, body, footer, days, datesCache) { + var renderAddDays = function(header, body, footer, days, datesCache, isAbleToMessage) { var messagesContainer = getMessagesContainer(body); var daysRenderPromises = days.map(function(data) { var timestampDate = new Date(data.value.timestamp * 1000); @@ -589,6 +707,7 @@ function( // Wait until all of the rendering is done for each of the days // to ensure they are added to the page in the correct order. days.forEach(function(data, index) { + // eslint-disable-next-line promise/no-nesting daysRenderPromises[index] .then(function(html) { if (data.before) { @@ -598,6 +717,12 @@ function( return messagesContainer.append(html); } }) + .always(function() { + const showUnableToMessage = !isAbleToMessage && index === days.length - 1; + messagesContainer.find(SELECTORS.DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER) + .last().toggleClass('hidden', !showUnableToMessage); + hideUnableToMessage(body); + }) .catch(function() { // Fail silently. }); @@ -823,7 +948,7 @@ function( if (hasAddDays) { renderingPromises.push(datesCachePromise.then(function(datesCache) { - return renderAddDays(header, body, footer, data.days.add, datesCache); + return renderAddDays(header, body, footer, data.days.add, datesCache, !data.unableToMessage); })); } @@ -919,8 +1044,6 @@ function( return showFooterContent(footer); case 'unblock': return showFooterRequireUnblock(footer); - case 'unable-to-message': - return showFooterUnableToMessage(footer); } return true; @@ -1644,6 +1767,29 @@ function( return true; }; + /** + * Show or hide the unable to message container and enable or disable the footer messaging controls. + * + * @param {Object} header The header container element. + * @param {Object} body The body container element. + * @param {Object} footer The footer container element. + * @param {Object} disable should the message be displayed?. + * @return {Object|true} jQuery promise + */ + var renderUnableToMessage = function(header, body, footer, disable) { + if (disable) { + disableAllFooterContentElements(footer); + if (!body.find(SELECTORS.DAY_MESSAGE_UNABLE_TO_SEND_CONTAINER).length) { + showUnableToMessage(body); + } + } else { + enableAllFooterContentElements(footer); + hideUnableToMessage(body); + } + + return true; + }; + /** * Show or hide the require add contact panel. * @@ -1684,6 +1830,8 @@ function( showHeaderPlaceholder(header); hideAllFooterElements(footer); showFooterPlaceholder(footer); + enableAllFooterContentElements(footer); + hideUnableToMessage(body); return true; }; @@ -1708,7 +1856,8 @@ function( confirmContactRequest: renderConfirmContactRequest, requireAddContact: renderRequireAddContact, selfConversationMessage: renderSelfConversationMessage, - contactRequestSent: renderContactRequestSent + contactRequestSent: renderContactRequestSent, + unableToMessage: renderUnableToMessage, }, { loadingMembers: renderLoadingMembers, diff --git a/public/message/amd/src/message_drawer_view_conversation_state_manager.js b/public/message/amd/src/message_drawer_view_conversation_state_manager.js index c07e6c96165..37cfb15405b 100644 --- a/public/message/amd/src/message_drawer_view_conversation_state_manager.js +++ b/public/message/amd/src/message_drawer_view_conversation_state_manager.js @@ -147,7 +147,8 @@ define(['jquery'], function($) { pendingDeleteConversation: false, selectedMessageIds: [], showEmojiAutoComplete: false, - showEmojiPicker: false + showEmojiPicker: false, + canSendMessageToConversation: true, }; }; @@ -836,6 +837,19 @@ define(['jquery'], function($) { return newState; }; + /** + * Set whether the user can send messages to the conversation. + * + * @param {Object} state Current state. + * @param {Bool} value If it can send message to conversation. + * @return {Object} New state. + */ + const setCanSendMessageToConversation = function(state, value) { + const newState = cloneState(state); + newState.canSendMessageToConversation = value; + return newState; + }; + return { buildInitialState: buildInitialState, addMessages: addMessages, @@ -877,6 +891,7 @@ define(['jquery'], function($) { removeSelectedMessagesById: removeSelectedMessagesById, markMessagesAsRead: markMessagesAsRead, addContactRequests: addContactRequests, - removeContactRequests: removeContactRequests + removeContactRequests: removeContactRequests, + setCanSendMessageToConversation: setCanSendMessageToConversation, }; }); diff --git a/public/message/classes/api.php b/public/message/classes/api.php index 8ee6246638e..32c3941cef9 100644 --- a/public/message/classes/api.php +++ b/public/message/classes/api.php @@ -771,6 +771,7 @@ class api { $conv->unreadcount = isset($unreadcounts[$conv->id]) ? $unreadcounts[$conv->id]->unreadcount : null; $conv->ismuted = $conversation->ismuted ? true : false; $conv->members = $members[$conv->id]; + $conv->cansendmessagetoconversation = self::can_send_message_to_conversation($userid, $conv->id); // Add the most recent message information. $conv->messages = []; @@ -956,7 +957,8 @@ class api { 'ismuted' => $ismuted, 'members' => $members, 'messages' => $messages['messages'], - 'candeletemessagesforallusers' => has_capability('moodle/site:deleteanymessage', $deleteallcontext) + 'candeletemessagesforallusers' => has_capability('moodle/site:deleteanymessage', $deleteallcontext), + 'cansendmessagetoconversation' => self::can_send_message_to_conversation($userid, $conversation->id), ]; } diff --git a/public/message/externallib.php b/public/message/externallib.php index 027adcc45d3..c4cd6dda57a 100644 --- a/public/message/externallib.php +++ b/public/message/externallib.php @@ -1043,6 +1043,8 @@ class core_message_external extends external_api { ), 'candeletemessagesforallusers' => new external_value(PARAM_BOOL, 'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false), + 'cansendmessagetoconversation' => new external_value(PARAM_BOOL, + 'If the user can send messages in the conversation', VALUE_DEFAULT, true), ) ); } diff --git a/public/message/templates/message_drawer_view_conversation_body.mustache b/public/message/templates/message_drawer_view_conversation_body.mustache index ea596d3df1c..31ec3a1868f 100644 --- a/public/message/templates/message_drawer_view_conversation_body.mustache +++ b/public/message/templates/message_drawer_view_conversation_body.mustache @@ -54,7 +54,11 @@ + + \ No newline at end of file diff --git a/public/message/templates/message_drawer_view_conversation_footer.mustache b/public/message/templates/message_drawer_view_conversation_footer.mustache index d330397ff77..5d8b91aa284 100644 --- a/public/message/templates/message_drawer_view_conversation_footer.mustache +++ b/public/message/templates/message_drawer_view_conversation_footer.mustache @@ -53,9 +53,6 @@ -
    {{> core_message/message_drawer_view_conversation_footer_placeholder }}
    diff --git a/public/message/templates/message_drawer_view_conversation_footer_unable_to_message.mustache b/public/message/templates/message_drawer_view_conversation_footer_unable_to_message.mustache deleted file mode 100644 index be2a91b1766..00000000000 --- a/public/message/templates/message_drawer_view_conversation_footer_unable_to_message.mustache +++ /dev/null @@ -1,40 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template core_message/message_drawer_view_conversation_footer_unable_to_message - - This template will render the footer content when the user is unable to message - in a conversation in the message drawer. - - Classes required for JS: - * none - - Data attributes required for JS: - * All data attributes are required - - Context variables required for this template: - * userid The logged in user id - * urls The URLs for the popover - - Example context (json): - {} - -}} - -
    -

    {{#str}} unabletomessage, core_message {{/str}}

    -
    diff --git a/public/message/tests/behat/block_user.feature b/public/message/tests/behat/block_user.feature index 2e503f2e66b..de0d68427c2 100644 --- a/public/message/tests/behat/block_user.feature +++ b/public/message/tests/behat/block_user.feature @@ -33,7 +33,7 @@ Feature: To be able to block users that we are able to or to see a message if we When I log in as "student2" And I open messaging And I select "Student 1" user in messaging - Then I should see "You are unable to message this user" + Then I should see "Cannot send message" Scenario: Unable to block a user Given I log in as "student1" @@ -58,7 +58,7 @@ Feature: To be able to block users that we are able to or to see a message if we And I log out And I log in as "student2" And I select "Student 1" user in messaging - And I should not see "You are unable to message this user" + And I should not see "Cannot send message" And I log out And I log in as "student1" And I select "Student 2" user in messaging diff --git a/public/message/tests/behat/message_send_messages.feature b/public/message/tests/behat/message_send_messages.feature index 9b701110049..c5051e324fe 100644 --- a/public/message/tests/behat/message_send_messages.feature +++ b/public/message/tests/behat/message_send_messages.feature @@ -12,10 +12,12 @@ Feature: Message send messages | username | firstname | lastname | email | | student1 | Student | 1 | student1@example.com | | student2 | Student | 2 | student2@example.com | + | teacher1 | Teacher | 1 | teacher1@example.com | And the following "course enrolments" exist: - | user | course | role | - | student1 | C1 | student | - | student2 | C1 | student | + | user | course | role | + | student1 | C1 | student | + | student2 | C1 | student | + | teacher1 | C1 | editingteacher | And the following "groups" exist: | name | course | idnumber | enablemessaging | | Group 1 | C1 | G1 | 1 | @@ -96,3 +98,19 @@ Feature: Message send messages Then I should see "You have an unsent message. It will be lost if you leave this page." And I press "Send message" And I should see "What you doing?" in the "Student 2" "core_message > Message conversation" + + Scenario: Student cannot reply to a message from a teacher not in their course + Given I log in as "teacher1" + And I open messaging + And I send "Hi!" message to "Student 1" user + And I am on "C1" course homepage + And I navigate to course participants + And I click on "Unenrol" "link" in the "student1" "table_row" + And I click on "Unenrol" "button" in the "Unenrol" "dialogue" + And I log in as "student1" + When I open messaging + And I select "Teacher 1" conversation in messaging + Then I should see "Cannot send message" + And the "disabled" attribute of "textarea[data-region='send-message-txt']" "css_element" should contain "true" + And the "disabled" attribute of "button[data-action='send-message']" "css_element" should contain "true" + And the "disabled" attribute of "button[data-action='toggle-emoji-picker']" "css_element" should contain "true" diff --git a/public/message/tests/externallib_test.php b/public/message/tests/externallib_test.php index f1c904229af..f4a92998372 100644 --- a/public/message/tests/externallib_test.php +++ b/public/message/tests/externallib_test.php @@ -5920,4 +5920,43 @@ final class externallib_test extends externallib_advanced_testcase { $result = external_api::clean_returnvalue(external\get_unsent_message::execute_returns(), $result); $this->assertEmpty($result); } + + /** + * Tests conversation messaging is restricted by each user’s messaging permission. + * + * @covers ::get_conversation + */ + public function test_get_conversation_send_message_permission(): void { + $this->resetAfterTest(); + + // Create some users. + $user1 = self::getDataGenerator()->create_user(); + $user2 = self::getDataGenerator()->create_user(); + $user3 = self::getDataGenerator()->create_user(); + + // Create a course and enrol user1 and user2. + $course1 = $this->getDataGenerator()->create_course(); + $this->setAdminUser(); + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + + // Conversation between two enrolled users. + $conversation1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + [$user1->id, $user2->id]); + // Conversation involving a non-enrolled user. + $conversation2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + [$user1->id, $user3->id]); + + // User2 should be able to send a message to user1. + $this->setUser($user2); + $conv = core_message_external::get_conversation($user2->id, $conversation1->id); + $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); + $this->assertTrue($conv['cansendmessagetoconversation']); + + // User3 should not be able to send a message to user1. + $this->setUser($user3); + $conv = core_message_external::get_conversation($user3->id, $conversation2->id); + $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); + $this->assertFalse($conv['cansendmessagetoconversation']); + } }