diff --git a/message/amd/build/message_drawer_view_conversation.min.js b/message/amd/build/message_drawer_view_conversation.min.js index eb2ce6c9b31..f3bcd8c1f73 100644 --- a/message/amd/build/message_drawer_view_conversation.min.js +++ b/message/amd/build/message_drawer_view_conversation.min.js @@ -1,2 +1,2 @@ -function _typeof(e){if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(e){return typeof e}}else{_typeof=function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e}}return _typeof(e)}define ("core_message/message_drawer_view_conversation",["jquery","core/auto_rows","core/backoff_timer","core/custom_interaction_events","core/notification","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/picker"],function(s,e,t,n,a,d,i,r,o,g,l,m,c,E,I,C){var _={},u=null,T=!1,A=0,f=null,h=!1,O=[],M=!0,N=!1,p=[],v=null,S=[],b=g.NEWEST_MESSAGES_FIRST,U=g.LOAD_MESSAGE_LIMIT,R=g.MILLISECONDS_IN_SEC,L=g.SELECTORS,D=g.CONVERSATION_TYPES,y=function(){if(!u||u.type==D.PUBLIC){return null}var e=u.loggedInUserId;if(u.type==D.SELF){return e}var s=Object.keys(u.members).filter(function(s){return e!=s});return s.length?s[0]:null},P=function(e){return Object.keys(_).reduce(function(s,t){if(!s){var n=_[t].state;if(n.type!=D.PUBLIC){if(e in n.members){s=n.id}}}return s},null)},B=function(e){return{id:parseInt(e.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:[]}},F=function(){return A},k=function(e){A=e;_[u.id].messagesOffset=e},V=function(){return T},w=function(e){T=e;_[u.id].loadedAllMessages=e},x=function(e){return e.find(L.MESSAGES_CONTAINER)},G=function(e){return{id:e.id,name:e.name,subname:e.subname,imageUrl:e.imageUrl,isFavourite:e.isFavourite,isMuted:e.isMuted,type:e.type,totalMemberCount:e.totalMemberCount,loggedInUserId:e.loggedInUserId,messages:e.messages.map(function(e){return s.extend({},e)}),members:Object.keys(e.members).map(function(t){var n=s.extend({},e.members[t]);n.contactrequests=e.members[t].contactrequests.map(function(e){return s.extend({},e)});return n})}},q=function(e,s){var t=e.id,n=t==s?D.SELF:D.PRIVATE,d=c.setLoadingMembers(u,!0);d=c.setLoadingMessages(d,!0);v(d);return r.getMemberInfo(t,[s],!0,!0).then(function(e){if(e.length){return e[0]}else{throw new Error("Unable to load other user profile")}}).then(function(s){var t=n==D.SELF?[s]:[s,e],a=c.addMembers(u,t);a=c.setLoadingMembers(a,!1);a=c.setLoadingMessages(a,!1);a=c.setName(a,s.fullname);a=c.setType(a,n);a=c.setImageUrl(a,s.profileimageurl);a=c.setTotalMemberCount(a,t.length);v(a);return s}).catch(function(e){var s=c.setLoadingMembers(u,!1);v(s);a.exception(e)})},Q=function(e,s){var t=null;if(e.type==D.PRIVATE){var n=e.members.filter(function(e){return e.id!=s});t=n.length?n[0]:null}else if(e.type==D.SELF){t=e.members[0]}var a=e.name,d=e.imageurl;if(e.type!=D.PUBLIC){a=a||t?t.fullname:"";d=d||t?t.profileimageurl:""}var i=c.addMembers(u,e.members);i=c.setName(i,a);i=c.setSubname(i,e.subname);i=c.setType(i,e.type);i=c.setImageUrl(i,d);i=c.setTotalMemberCount(i,e.membercount);i=c.setIsFavourite(i,e.isfavourite);i=c.setIsMuted(i,e.ismuted);i=c.addMessages(i,e.messages);i=c.setCanDeleteMessagesForAllUsers(i,e.candeletemessagesforallusers);return i},K=function(e,s,t,n,d){var i=s.id,o=c.setLoadingMembers(u,!0);o=c.setLoadingMessages(o,!0);v(o);return r.getConversation(i,e,!0,!0,0,0,t+1,n,d).then(function(e){if(e.messages.length>t){e.messages=e.messages.slice(1)}else{w(!0)}k(n+t);return e}).then(function(e){var t=e.members.filter(function(e){return e.id==s.id});if(1>t.length){e.members=e.members.concat([s])}var n=Q(e,s.id);n=c.setLoadingMembers(n,!1);n=c.setLoadingMessages(n,!1);return v(n).then(function(){return e})}).then(function(){return X(e)}).catch(function(e){var s=c.setLoadingMembers(u,!1);s=c.setLoadingMessages(s,!1);v(s);a.exception(e)})},j=function(e,s,t,n){var d=e.members.filter(function(e){return e.id==s.id});if(1>d.length){e.members=e.members.concat([s])}var i=e.messages.length,r=i>=t,o=Q(e,s.id);o=c.setLoadingMembers(o,!1);o=c.setLoadingMessages(o,!r);var g=v(o);return g.then(function(){if(!r){return W(e.id,t,i,n,[])}else{return{messages:e.messages}}}).then(function(){var e=u.messages;k(e.length);X(u.id);return e}).catch(a.exception)},W=function(e,s,t,n,a,d){return r.getMessages(u.loggedInUserId,e,s?s+1:s,t,n,d).then(function(e){if(e.messages.length&&a.length){e.messages=e.messages.filter(function(e){return 0>a.indexOf(parseInt(e.id,10))})}return e}).then(function(e){if(!s){return e}else if(e.messages.length>s){e.messages=e.messages.slice(0,-1)}else{w(!0)}return e}).then(function(e){var s=e.members.filter(function(e){return!(e.id in u.members)}),t=c.addMembers(u,s);t=c.addMessages(t,e.messages);t=c.setLoadingMessages(t,!1);return v(t).then(function(){return e})}).catch(function(e){var s=c.setLoadingMessages(u,!1);v(s);throw e})},J=function(e,t){return function(){var n=u.messages,a=n.length?n[n.length-1]:null,r=a?a.timeCreated:null;if(r&&!M&&!N){for(var g=[],l=n.length-1,m;0<=l;l--){m=n[l];if(m.timeCreated===r){g.push(m.id)}else{break}}return W(e,0,0,t,g,r).then(function(s){if(s.messages.length){f.restart();var t=G(u);d.publish(o.CONVERSATION_NEW_LAST_MESSAGE,t);return X(e)}else{return s}})}return s.Deferred().resolve().promise()}},X=function(e){var s=u.loggedInUserId;return r.markAllConversationMessagesAsRead(s,e).then(function(){var s=c.markMessagesAsRead(u,u.messages);d.publish(o.CONVERSATION_READ,e);return v(s)})},Y=function(e){me(e);var s=c.addPendingBlockUsersById(u,[e]);v(s)},H=function(e){var s=c.setLoadingConfirmAction(u,!0);v(s);return r.blockUser(u.loggedInUserId,e).then(function(s){var t=c.addMembers(u,[s]);t=c.removePendingBlockUsersById(t,[e]);t=c.setLoadingConfirmAction(t,!1);d.publish(o.CONTACT_BLOCKED,e);return v(t)})},z=function(e){me(e);var s=c.addPendingUnblockUsersById(u,[e]);v(s)},Z=function(e){var s=c.setLoadingConfirmAction(u,!0);v(s);return r.unblockUser(u.loggedInUserId,e).then(function(s){var t=c.addMembers(u,[s]);t=c.removePendingUnblockUsersById(t,[e]);t=c.setLoadingConfirmAction(t,!1);d.publish(o.CONTACT_UNBLOCKED,e);return v(t)})},$=function(e){me(e);var s=c.addPendingRemoveContactsById(u,[e]);v(s)},ee=function(e){var s=c.setLoadingConfirmAction(u,!0);v(s);return r.deleteContacts(u.loggedInUserId,[e]).then(function(s){var t=c.addMembers(u,s);t=c.removePendingRemoveContactsById(t,[e]);t=c.setLoadingConfirmAction(t,!1);d.publish(o.CONTACT_REMOVED,e);return v(t)})},se=function(e){me(e);var s=c.addPendingAddContactsById(u,[e]);v(s)},te=function(e){var s=c.setLoadingConfirmAction(u,!0);v(s);return r.createContactRequest(u.loggedInUserId,e).then(function(e){if(!e.request){throw new Error(e.warnings[0].message)}return e.request}).then(function(s){var t=c.removePendingAddContactsById(u,[e]);t=c.addContactRequests(t,[s]);t=c.setLoadingConfirmAction(t,!1);return v(t)})},ne=function(){var e=u.loggedInUserId,s=u.id;return r.setFavouriteConversations(e,[s]).then(function(){var e=c.setIsFavourite(u,!0);return v(e)}).then(function(){return d.publish(o.CONVERSATION_SET_FAVOURITE,G(u))})},ae=function(){var e=u.loggedInUserId,s=u.id;return r.unsetFavouriteConversations(e,[s]).then(function(){var e=c.setIsFavourite(u,!1);return v(e)}).then(function(){return d.publish(o.CONVERSATION_UNSET_FAVOURITE,G(u))})},de=function(){var e=u.loggedInUserId,s=u.id;return r.setMutedConversations(e,[s]).then(function(){var e=c.setIsMuted(u,!0);return v(e)}).then(function(){return d.publish(o.CONVERSATION_SET_MUTED,G(u))})},ie=function(){var e=u.loggedInUserId,s=u.id;return r.unsetMutedConversations(e,[s]).then(function(){var e=c.setIsMuted(u,!1);return v(e)}).then(function(){return d.publish(o.CONVERSATION_UNSET_MUTED,G(u))})},re=function(e){var s=u.selectedMessageIds;me(e);var t=c.addPendingDeleteMessagesById(u,s);v(t)},oe=function(){var e=u.pendingDeleteMessageIds,t=u.messages.filter(function(s){return 0<=e.indexOf(s.id)&&("sent"==s.sendState||null===s.sendState)}),n=c.setLoadingConfirmAction(u,!0);v(n);var i=s.Deferred().resolve().promise();if(t.length){var g=t.map(function(e){return e.id});if(n.deleteMessagesForAllUsers){i=r.deleteMessagesForAllUsers(u.loggedInUserId,g)}else{i=r.deleteMessages(u.loggedInUserId,g)}}return i.then(function(){var s=c.removeMessagesById(u,e);s=c.removePendingDeleteMessagesById(s,e);s=c.removeSelectedMessagesById(s,e);s=c.setLoadingConfirmAction(s,!1);s=c.setDeleteMessagesForAllUsers(s,!1);var t=u.messages[u.messages.length-1],n=s.messages.length?s.messages[s.messages.length-1]:null;if(n&&n.id!=t.id){var a=G(s);d.publish(o.CONVERSATION_NEW_LAST_MESSAGE,a)}else if(!s.messages.length){d.publish(o.CONVERSATION_DELETED,s.id)}return v(s)}).catch(a.exception)},ge=function(e){me(e);var s=c.setPendingDeleteConversation(u,!0);v(s)},le=function(){var e=c.setLoadingConfirmAction(u,!0);v(e);return r.deleteConversation(u.loggedInUserId,u.id).then(function(){var e=c.removeMessages(u,u.messages);e=c.removeSelectedMessagesById(e,u.selectedMessageIds);e=c.setPendingDeleteConversation(e,!1);e=c.setLoadingConfirmAction(e,!1);d.publish(o.CONVERSATION_DELETED,e.id);return v(e)})},me=function(e){var s=u.pendingDeleteMessageIds,t=c.removePendingAddContactsById(u,[e]);t=c.removePendingRemoveContactsById(t,[e]);t=c.removePendingUnblockUsersById(t,[e]);t=c.removePendingBlockUsersById(t,[e]);t=c.removePendingDeleteMessagesById(t,s);t=c.setPendingDeleteConversation(t,!1);t=c.setDeleteMessagesForAllUsers(t,!1);v(t)},ce=function(e){var s=u.loggedInUserId,t=u.members[e].contactrequests.filter(function(e){return e.requesteduserid==s}),n=t[0],a=c.setLoadingConfirmAction(u,!0);v(a);return r.acceptContactRequest(e,s).then(function(e){var s=c.removeContactRequests(u,[n]);s=c.addMembers(u,[e]);s=c.setLoadingConfirmAction(s,!1);return v(s)}).then(function(){d.publish(o.CONTACT_ADDED,u.members[e]);d.publish(o.CONTACT_REQUEST_ACCEPTED,n)})},Ee=function(e){var s=u.loggedInUserId,t=u.members[e].contactrequests.filter(function(e){return e.requesteduserid==s}),n=t[0],a=c.setLoadingConfirmAction(u,!0);v(a);return r.declineContactRequest(e,s).then(function(e){var s=c.removeContactRequests(u,[n]);s=c.addMembers(u,[e]);s=c.setLoadingConfirmAction(s,!1);return v(s)}).then(function(){d.publish(o.CONTACT_REQUEST_DECLINED,n)})},Ie=function(){if(N){return}if(!p.length){return}N=!0;var e=p.slice();p=[];var t=u.id,n=null,a=e.map(function(e){return e.text}),g=e.map(function(e){return e.id}),l=null,m=null;if(!t&&u.type!=D.PUBLIC){var E=y();l=r.sendMessagesToUser(E,a).then(function(e){if(e.length){n=parseInt(e[0].conversationid,10);m=e[0].candeletemessagesforallusers}return e})}else{l=r.sendMessagesToConversation(t,a)}l.then(function(s){var t=s.map(function(e){return e.id}),a=[],i=[],r=[];e.forEach(function(e,t){var n=s[t];a.push([e,n]);if(0<=u.selectedMessageIds.indexOf(e.id)){i.push(e.id);r.push(n.id)}});var g=c.updateMessages(u,a);g=c.setMessagesSendSuccessById(g,t);if(i.length){g=c.removeSelectedMessagesById(g,i)}if(r.length){g=c.addSelectedMessagesById(g,r)}var l=G(g);if(!g.id){g=c.setId(g,n);l.id=n;Fe(n);d.publish(o.CONVERSATION_CREATED,l);g=c.setCanDeleteMessagesForAllUsers(g,m)}v(g);N=!1;Ie();d.publish(o.CONVERSATION_NEW_LAST_MESSAGE,l)}).catch(function(t){if(t.message){var e=s.Deferred().resolve(t.message).promise()}else{var e=i.get_string("unknownerror","core")}var n=function(e){var s=c.setMessagesSendFailById(u,g,e);v(s);N=!1;Ie()};e.then(n).catch(function(s){var e=s.message||"Something went wrong!";n(e)})})},Ce=function(e){var s="temp"+Date.now(),t={id:s,useridfrom:u.loggedInUserId,text:e,timecreated:null},n=c.addMessages(u,[t]);v(n);p.push(t);Ie()},_e=function(e){var s=c.setMessagesSendPendingById(u,[e.id]);v(s);p.push(e);Ie()},ue=function(e){var s=u;if(-1t){e.messages=e.messages.slice(1)}else{x(!0)}k(n+t);return e}).then(function(e){var t=e.members.filter(function(e){return e.id==s.id});if(1>t.length){e.members=e.members.concat([s])}var n=K(e,s.id);n=c.setLoadingMembers(n,!1);n=c.setLoadingMessages(n,!1);return v(n).then(function(){return e})}).then(function(){return Y(e)}).catch(function(e){var s=c.setLoadingMembers(T,!1);s=c.setLoadingMessages(s,!1);v(s);a.exception(e)})},W=function(e,s,t,n){var d=e.members.filter(function(e){return e.id==s.id});if(1>d.length){e.members=e.members.concat([s])}var i=e.messages.length,o=i>=t,r=K(e,s.id);r=c.setLoadingMembers(r,!1);r=c.setLoadingMessages(r,!o);var g=v(r);return g.then(function(){if(!o){return J(e.id,t,i,n,[])}else{return{messages:e.messages}}}).then(function(){var e=T.messages;k(e.length);Y(T.id);return e}).catch(a.exception)},J=function(e,s,t,n,a,d){return o.getMessages(T.loggedInUserId,e,s?s+1:s,t,n,d).then(function(e){if(e.messages.length&&a.length){e.messages=e.messages.filter(function(e){return 0>a.indexOf(parseInt(e.id,10))})}return e}).then(function(e){if(!s){return e}else if(e.messages.length>s){e.messages=e.messages.slice(0,-1)}else{x(!0)}return e}).then(function(e){var s=e.members.filter(function(e){return!(e.id in T.members)}),t=c.addMembers(T,s);t=c.addMessages(t,e.messages);t=c.setLoadingMessages(t,!1);return v(t).then(function(){return e})}).catch(function(e){var s=c.setLoadingMessages(T,!1);v(s);throw e})},X=function(e,t){return function(){var n=T.messages,a=n.length?n[n.length-1]:null,o=a?a.timeCreated:null;if(o&&!p&&!N){for(var g=[],l=n.length-1,m;0<=l;l--){m=n[l];if(m.timeCreated===o){g.push(m.id)}else{break}}return J(e,0,0,t,g,o).then(function(s){if(s.messages.length){h.restart();var t=q(T);d.publish(r.CONVERSATION_NEW_LAST_MESSAGE,t);return Y(e)}else{return s}})}return s.Deferred().resolve().promise()}},Y=function(e){var s=T.loggedInUserId;return o.markAllConversationMessagesAsRead(s,e).then(function(){var s=c.markMessagesAsRead(T,T.messages);d.publish(r.CONVERSATION_READ,e);return v(s)})},H=function(e){ce(e);var s=c.addPendingBlockUsersById(T,[e]);v(s)},z=function(e){var s=c.setLoadingConfirmAction(T,!0);v(s);return o.blockUser(T.loggedInUserId,e).then(function(s){var t=c.addMembers(T,[s]);t=c.removePendingBlockUsersById(t,[e]);t=c.setLoadingConfirmAction(t,!1);d.publish(r.CONTACT_BLOCKED,e);return v(t)})},Z=function(e){ce(e);var s=c.addPendingUnblockUsersById(T,[e]);v(s)},$=function(e){var s=c.setLoadingConfirmAction(T,!0);v(s);return o.unblockUser(T.loggedInUserId,e).then(function(s){var t=c.addMembers(T,[s]);t=c.removePendingUnblockUsersById(t,[e]);t=c.setLoadingConfirmAction(t,!1);d.publish(r.CONTACT_UNBLOCKED,e);return v(t)})},ee=function(e){ce(e);var s=c.addPendingRemoveContactsById(T,[e]);v(s)},se=function(e){var s=c.setLoadingConfirmAction(T,!0);v(s);return o.deleteContacts(T.loggedInUserId,[e]).then(function(s){var t=c.addMembers(T,s);t=c.removePendingRemoveContactsById(t,[e]);t=c.setLoadingConfirmAction(t,!1);d.publish(r.CONTACT_REMOVED,e);return v(t)})},te=function(e){ce(e);var s=c.addPendingAddContactsById(T,[e]);v(s)},ne=function(e){var s=c.setLoadingConfirmAction(T,!0);v(s);return o.createContactRequest(T.loggedInUserId,e).then(function(e){if(!e.request){throw new Error(e.warnings[0].message)}return e.request}).then(function(s){var t=c.removePendingAddContactsById(T,[e]);t=c.addContactRequests(t,[s]);t=c.setLoadingConfirmAction(t,!1);return v(t)})},ae=function(){var e=T.loggedInUserId,s=T.id;return o.setFavouriteConversations(e,[s]).then(function(){var e=c.setIsFavourite(T,!0);return v(e)}).then(function(){return d.publish(r.CONVERSATION_SET_FAVOURITE,q(T))})},de=function(){var e=T.loggedInUserId,s=T.id;return o.unsetFavouriteConversations(e,[s]).then(function(){var e=c.setIsFavourite(T,!1);return v(e)}).then(function(){return d.publish(r.CONVERSATION_UNSET_FAVOURITE,q(T))})},ie=function(){var e=T.loggedInUserId,s=T.id;return o.setMutedConversations(e,[s]).then(function(){var e=c.setIsMuted(T,!0);return v(e)}).then(function(){return d.publish(r.CONVERSATION_SET_MUTED,q(T))})},oe=function(){var e=T.loggedInUserId,s=T.id;return o.unsetMutedConversations(e,[s]).then(function(){var e=c.setIsMuted(T,!1);return v(e)}).then(function(){return d.publish(r.CONVERSATION_UNSET_MUTED,q(T))})},re=function(e){var s=T.selectedMessageIds;ce(e);var t=c.addPendingDeleteMessagesById(T,s);v(t)},ge=function(){var e=T.pendingDeleteMessageIds,t=T.messages.filter(function(s){return 0<=e.indexOf(s.id)&&("sent"==s.sendState||null===s.sendState)}),n=c.setLoadingConfirmAction(T,!0);v(n);var i=s.Deferred().resolve().promise();if(t.length){var g=t.map(function(e){return e.id});if(n.deleteMessagesForAllUsers){i=o.deleteMessagesForAllUsers(T.loggedInUserId,g)}else{i=o.deleteMessages(T.loggedInUserId,g)}}return i.then(function(){var s=c.removeMessagesById(T,e);s=c.removePendingDeleteMessagesById(s,e);s=c.removeSelectedMessagesById(s,e);s=c.setLoadingConfirmAction(s,!1);s=c.setDeleteMessagesForAllUsers(s,!1);var t=T.messages[T.messages.length-1],n=s.messages.length?s.messages[s.messages.length-1]:null;if(n&&n.id!=t.id){var a=q(s);d.publish(r.CONVERSATION_NEW_LAST_MESSAGE,a)}else if(!s.messages.length){d.publish(r.CONVERSATION_DELETED,s.id)}return v(s)}).catch(a.exception)},le=function(e){ce(e);var s=c.setPendingDeleteConversation(T,!0);v(s)},me=function(){var e=c.setLoadingConfirmAction(T,!0);v(e);return o.deleteConversation(T.loggedInUserId,T.id).then(function(){var e=c.removeMessages(T,T.messages);e=c.removeSelectedMessagesById(e,T.selectedMessageIds);e=c.setPendingDeleteConversation(e,!1);e=c.setLoadingConfirmAction(e,!1);d.publish(r.CONVERSATION_DELETED,e.id);return v(e)})},ce=function(e){var s=T.pendingDeleteMessageIds,t=c.removePendingAddContactsById(T,[e]);t=c.removePendingRemoveContactsById(t,[e]);t=c.removePendingUnblockUsersById(t,[e]);t=c.removePendingBlockUsersById(t,[e]);t=c.removePendingDeleteMessagesById(t,s);t=c.setPendingDeleteConversation(t,!1);t=c.setDeleteMessagesForAllUsers(t,!1);v(t)},Ee=function(e){var s=T.loggedInUserId,t=T.members[e].contactrequests.filter(function(e){return e.requesteduserid==s}),n=t[0],a=c.setLoadingConfirmAction(T,!0);v(a);return o.acceptContactRequest(e,s).then(function(e){var s=c.removeContactRequests(T,[n]);s=c.addMembers(T,[e]);s=c.setLoadingConfirmAction(s,!1);return v(s)}).then(function(){d.publish(r.CONTACT_ADDED,T.members[e]);d.publish(r.CONTACT_REQUEST_ACCEPTED,n)})},Ie=function(e){var s=T.loggedInUserId,t=T.members[e].contactrequests.filter(function(e){return e.requesteduserid==s}),n=t[0],a=c.setLoadingConfirmAction(T,!0);v(a);return o.declineContactRequest(e,s).then(function(e){var s=c.removeContactRequests(T,[n]);s=c.addMembers(T,[e]);s=c.setLoadingConfirmAction(s,!1);return v(s)}).then(function(){d.publish(r.CONTACT_REQUEST_DECLINED,n)})},Ce=function(){if(N){return}if(!S.length){return}N=!0;var e=S.slice();S=[];var t=T.id,n=null,a=e.map(function(e){return e.text}),g=e.map(function(e){return e.id}),l=null,m=null;if(!t&&T.type!=y.PUBLIC){var E=P();l=o.sendMessagesToUser(E,a).then(function(e){if(e.length){n=parseInt(e[0].conversationid,10);m=e[0].candeletemessagesforallusers}return e})}else{l=o.sendMessagesToConversation(t,a)}l.then(function(s){var t=s.map(function(e){return e.id}),a=[],i=[],o=[];e.forEach(function(e,t){var n=s[t];a.push([e,n]);if(0<=T.selectedMessageIds.indexOf(e.id)){i.push(e.id);o.push(n.id)}});var g=c.updateMessages(T,a);g=c.setMessagesSendSuccessById(g,t);if(i.length){g=c.removeSelectedMessagesById(g,i)}if(o.length){g=c.addSelectedMessagesById(g,o)}var l=q(g);if(!g.id){g=c.setId(g,n);l.id=n;we(n);d.publish(r.CONVERSATION_CREATED,l);g=c.setCanDeleteMessagesForAllUsers(g,m)}v(g);N=!1;Ce();d.publish(r.CONVERSATION_NEW_LAST_MESSAGE,l)}).catch(function(t){if(t.message){var e=s.Deferred().resolve(t.message).promise()}else{var e=i.get_string("unknownerror","core")}var n=function(e){var s=c.setMessagesSendFailById(T,g,e);v(s);N=!1;Ce()};e.then(n).catch(function(s){var e=s.message||"Something went wrong!";n(e)})})},_e=function(e){var s="temp"+Date.now(),t={id:s,useridfrom:T.loggedInUserId,text:e,timecreated:null},n=c.addMessages(T,[t]);v(n);S.push(t);Ce()},ue=function(e){var s=c.setMessagesSendPendingById(T,[e.id]);v(s);S.push(e);Ce()},Te=function(e){var s=T;if(-1.\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/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/picker'\n],\nfunction(\n $,\n AutoRows,\n BackOffTimer,\n CustomEvents,\n Notification,\n PubSub,\n Str,\n Repository,\n MessageDrawerEvents,\n Constants,\n Patcher,\n Renderer,\n StateManager,\n MessageDrawerRouter,\n MessageDrawerRoutes,\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 // 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 };\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 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) {\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\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 };\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 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 };\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 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 };\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 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 };\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 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 };\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\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 };\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\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 };\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\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 };\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 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 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 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 return render(newState);\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 newState = StateManager.setLoadingConfirmAction(viewState, true);\n render(newState);\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 return render(newState);\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 // 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 };\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 // 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 };\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 // 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 .catch(function(e) {\n if (e.message) {\n var errorMessage = $.Deferred().resolve(e.message).promise();\n } else {\n var 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 .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 * 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 var message = {\n id: id,\n useridfrom: viewState.loggedInUserId,\n text: text,\n timecreated: null\n };\n var newState = StateManager.addMessages(viewState, [message]);\n render(newState);\n sendMessageBuffer.push(message);\n processSendMessageBuffer();\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 * @return {Promise} Renderer promise.\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 .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 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 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 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 loadedAllMessages = false;\n messagesOffset = 0;\n newMessagesPollTimer = null;\n isRendering = false;\n renderBuffer = [];\n isResetting = true;\n isSendingMessage = 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 if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\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 // 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 return {\n show: show,\n description: description\n };\n});\n"],"file":"message_drawer_view_conversation.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/message_drawer_view_conversation.js"],"names":["define","$","AutoRows","BackOffTimer","CustomEvents","Notification","PubSub","Str","Repository","MessageDrawerEvents","Constants","Patcher","Renderer","StateManager","MessageDrawerRouter","MessageDrawerRoutes","initialiseEmojiAutoComplete","initialiseEmojiPicker","stateCache","viewState","loadedAllMessages","messagesOffset","newMessagesPollTimer","isRendering","renderBuffer","isResetting","isSendingMessage","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","getCachedPrivateConversationIdFromUserId","reduce","carry","id","state","getLoggedInUserProfile","body","parseInt","attr","fullname","profileimageurl","profileimageurlsmall","isonline","showonlinestatus","isblocked","iscontact","isdeleted","canmessage","canmessageevenifblocked","requirescontact","contactrequests","getMessagesOffset","setMessagesOffset","value","hasLoadedAllMessages","setLoadedAllMessages","getMessagesContainer","find","MESSAGES_CONTAINER","formatConversationForEvent","name","subname","imageUrl","isFavourite","isMuted","totalMemberCount","messages","map","message","extend","formattedMember","request","loadEmptyPrivateConversation","loggedInUserProfile","otherUserId","conversationType","PRIVATE","newState","setLoadingMembers","setLoadingMessages","getMemberInfo","then","profiles","Error","profile","addMembers","setName","setType","setImageUrl","setTotalMemberCount","catch","error","exception","updateStateFromConversation","conversation","otherUser","otherUsers","member","imageurl","setSubname","membercount","setIsFavourite","isfavourite","setIsMuted","ismuted","addMessages","setCanDeleteMessagesForAllUsers","candeletemessagesforallusers","loadNewConversation","conversationId","messageLimit","messageOffset","newestFirst","getConversation","slice","hasLoggedInUser","concat","markConversationAsRead","loadExistingConversation","messageCount","hasLoadedEnoughMessages","renderPromise","loadMessages","limit","offset","ignoreList","timeFrom","getMessages","result","indexOf","membersToAdd","getLoadNewMessagesCallback","mostRecentMessage","lastTimeCreated","timeCreated","ignoreMessageIds","i","push","restart","publish","CONVERSATION_NEW_LAST_MESSAGE","Deferred","resolve","promise","markAllConversationMessagesAsRead","markMessagesAsRead","CONVERSATION_READ","requestBlockUser","cancelRequest","addPendingBlockUsersById","blockUser","setLoadingConfirmAction","removePendingBlockUsersById","CONTACT_BLOCKED","requestUnblockUser","addPendingUnblockUsersById","unblockUser","removePendingUnblockUsersById","CONTACT_UNBLOCKED","requestRemoveContact","addPendingRemoveContactsById","removeContact","deleteContacts","removePendingRemoveContactsById","CONTACT_REMOVED","requestAddContact","addPendingAddContactsById","addContact","createContactRequest","response","warnings","removePendingAddContactsById","addContactRequests","setFavourite","setFavouriteConversations","CONVERSATION_SET_FAVOURITE","unsetFavourite","unsetFavouriteConversations","CONVERSATION_UNSET_FAVOURITE","setMuted","setMutedConversations","CONVERSATION_SET_MUTED","unsetMuted","unsetMutedConversations","CONVERSATION_UNSET_MUTED","requestDeleteSelectedMessages","selectedMessageIds","addPendingDeleteMessagesById","deleteSelectedMessages","messageIds","pendingDeleteMessageIds","sentMessages","sendState","deleteMessagesPromise","sentMessageIds","deleteMessagesForAllUsers","deleteMessages","removeMessagesById","removePendingDeleteMessagesById","removeSelectedMessagesById","setDeleteMessagesForAllUsers","prevLastMessage","newLastMessage","CONVERSATION_DELETED","requestDeleteConversation","setPendingDeleteConversation","deleteConversation","removeMessages","acceptContactRequest","requests","requesteduserid","removeContactRequests","CONTACT_ADDED","CONTACT_REQUEST_ACCEPTED","declineContactRequest","CONTACT_REQUEST_DECLINED","processSendMessageBuffer","messagesToSend","newConversationId","messagesText","text","sendMessagePromise","newCanDeleteMessagesForAllUsers","sendMessagesToUser","conversationid","sendMessagesToConversation","newMessageIds","data","selectedToRemove","selectedToAdd","forEach","oldMessage","index","newMessage","updateMessages","setMessagesSendSuccessById","addSelectedMessagesById","setId","resetMessagePollTimer","CONVERSATION_CREATED","e","errorMessage","get_string","handleFailedMessages","setMessagesSendFailById","finalError","sendMessage","Date","now","useridfrom","timecreated","retrySendMessage","setMessagesSendPendingById","toggleSelectMessage","messageId","cancelEditMode","processRenderBuffer","header","footer","renderable","shift","renderPromises","renderFunc","patch","when","apply","deferred","reject","generateRenderFunction","isNewConversation","rendererFunc","initialState","buildInitialState","midnight","syncPatch","buildPatch","generateConfirmActionHandler","actionCallback","loadingConfirmAction","originalEvent","preventDefault","handleSendMessage","target","footerContainer","closest","FOOTER_CONTAINER","textArea","MESSAGE_TEXT_AREA","val","trim","focus","handleSelectMessage","selection","window","getSelection","toString","is","element","MESSAGE","handleRetrySendMessage","stopPropagation","handleCancelEditMode","generateHandleViewContact","namespace","go","VIEW_CONTACT","handleSetFavourite","handleUnsetFavourite","handleSetMuted","handleUnsetMuted","handleDeleteMessagesForAllUsersToggle","newValue","prop","generateHandleViewGroupInfo","VIEW_GROUP_INFO","handleToggleEmojiPicker","setShowEmojiPicker","showEmojiPicker","handleCloseEmojiPicker","EMOJI_PICKER_CONTAINER","TOGGLE_EMOJI_PICKER_BUTTON","registerEventListeners","isLoadingMoreMessages","messagesContainer","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","replace","textAfter","events","activate","enter","escape","scrollTop","scrollLock","on","hasMembers","handler","selector","handlerFunction","enterToSend","document","subscribe","ROUTE_CHANGED","newRouteData","route","VIEW_CONVERSATION","stop","getIncrementalCallback","messagePollMin","messagePollMax","messagePollAfterMax","start","resetState","resetNoConversation","resetNoConversationPromise","getConversationBetweenUsers","getSelfConversation","resetByConversation","resetById","cache","show","conversationOrId","action","isNaN","CAN_RECEIVE_FOCUS","first","currentOtherUserId","description"],"mappings":"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","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/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 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 // 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 };\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 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) {\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\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 };\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 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 };\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 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 };\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 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 };\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 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 };\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\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 };\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\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 };\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\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 };\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 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 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 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 return render(newState);\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 newState = StateManager.setLoadingConfirmAction(viewState, true);\n render(newState);\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 return render(newState);\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 // 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 };\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 // 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 };\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 // 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 .catch(function(e) {\n if (e.message) {\n var errorMessage = $.Deferred().resolve(e.message).promise();\n } else {\n var 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 .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 * 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 var message = {\n id: id,\n useridfrom: viewState.loggedInUserId,\n text: text,\n timecreated: null\n };\n var newState = StateManager.addMessages(viewState, [message]);\n render(newState);\n sendMessageBuffer.push(message);\n processSendMessageBuffer();\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 * @return {Promise} Renderer promise.\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 .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 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 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 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 loadedAllMessages = false;\n messagesOffset = 0;\n newMessagesPollTimer = null;\n isRendering = false;\n renderBuffer = [];\n isResetting = true;\n isSendingMessage = 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 if (newMessagesPollTimer) {\n newMessagesPollTimer.stop();\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 // 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 return {\n show: show,\n description: description\n };\n});\n"],"file":"message_drawer_view_conversation.min.js"} \ No newline at end of file diff --git a/message/amd/build/message_drawer_view_conversation_constants.min.js b/message/amd/build/message_drawer_view_conversation_constants.min.js index 855afc17410..2157c187aa3 100644 --- a/message/amd/build/message_drawer_view_conversation_constants.min.js +++ b/message/amd/build/message_drawer_view_conversation_constants.min.js @@ -1,2 +1,2 @@ -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_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_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_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}}); //# sourceMappingURL=message_drawer_view_conversation_constants.min.js.map diff --git a/message/amd/build/message_drawer_view_conversation_constants.min.js.map b/message/amd/build/message_drawer_view_conversation_constants.min.js.map index f8a9827c81b..c63609d15f5 100644 --- a/message/amd/build/message_drawer_view_conversation_constants.min.js.map +++ b/message/amd/build/message_drawer_view_conversation_constants.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/message_drawer_view_conversation_constants.js"],"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_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_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":"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","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_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_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"],"file":"message_drawer_view_conversation_constants.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/message_drawer_view_conversation_constants.js"],"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_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":"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","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_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"],"file":"message_drawer_view_conversation_constants.min.js"} \ No newline at end of file diff --git a/message/amd/build/message_drawer_view_conversation_patcher.min.js b/message/amd/build/message_drawer_view_conversation_patcher.min.js index 4d79811c12a..899dbf3d1d4 100644 --- a/message/amd/build/message_drawer_view_conversation_patcher.min.js +++ b/message/amd/build/message_drawer_view_conversation_patcher.min.js @@ -1,2 +1,2 @@ -function _typeof(a){if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_message/message_drawer_view_conversation_patcher",["jquery","core/user_date","core_message/message_drawer_view_conversation_constants"],function(a,b,c){var d=function(a,c){var d=a.reduce(function(a,d){var e=d.timeCreated?d.timeCreated:c,f=b.getUserMidnightForTimestamp(e,c);if(a.hasOwnProperty(f)){a[f].push(d)}else{a[f]=[d]}return a},{});return Object.keys(d).map(function(a){return{timestamp:a,messages:d[a]}})},e=function(c,a,d){a=a.slice();var e=[],f=[],g=[];c.forEach(function(b){var c=!1,e=0;for(;ee&&1>f){return!0}if(e!=f){return!1}return c.every(function(a,b){return a==d[b]})},h=function(c,a){var b=Object.keys(c),d=Object.keys(a);if(b.length!=d.length){return!1}return b.every(function(b){var d=c[b],e=a[b],f=_typeof(d),i=_typeof(e);f=null===d?"null":f;i=null===d?"null":i;f="object"===f&&Array.isArray(f)?"array":f;i="object"===i&&Array.isArray(i)?"array":i;if(f!==i){return!1}switch(f){case"object":return h(d,e);case"array":return g(d,e);default:return c[b]==a[b];}})},i=function(c,a){return h({id:c.id,state:c.sendState,text:c.text,timeCreated:c.timeCreated},{id:a.id,state:a.sendState,text:a.text,timeCreated:a.timeCreated})},j=function(a,b,c){return{remove:b,add:c.map(function(b){var c=f(a,function(a){return b.timestampd.length){return null}if(1>c.length){return d[d.length-1].id}var e=c[a.messages.length-1],f=d[d.length-1],g=c[0],h=d[0];if(e.id!=f.id){return f.id}else if(g.id!=h.id){return g.id}return null},q=function(a,b){if(!a.loadingMembers&&b.loadingMembers){return!0}else if(a.loadingMembers&&!b.loadingMembers){return!1}else{return null}},r=function(a,b){if(a.hasTriedToLoadMessages===b.hasTriedToLoadMessages){return null}else if(!b.hasTriedToLoadMessages&&b.loadingMessages){return!0}else if(b.hasTriedToLoadMessages&&!b.loadingMessages){return!1}else{return null}},s=function(a,b){if(!a.loadingMessages&&b.loadingMessages){return!0}else if(a.loadingMessages&&!b.loadingMessages){return!1}else{return null}},t=function(a,b){if(!a.showEmojiPicker&&b.showEmojiPicker){return!0}else if(a.showEmojiPicker&&!b.showEmojiPicker){return!1}else{return null}},u=function(a,b){if(b.pendingBlockUserIds.length){var c=b.pendingBlockUserIds[0];return b.members[c]}else if(a.pendingBlockUserIds.length){return!1}return null},v=function(a,b){if(b.pendingUnblockUserIds.length){var c=b.pendingUnblockUserIds[0];return b.members[c]}else if(a.pendingUnblockUserIds.length){return!1}return null},w=function(a,b){if(b.pendingAddContactIds.length){var c=b.pendingAddContactIds[0];return b.members[c]}else if(a.pendingAddContactIds.length){return!1}return null},x=function(a,b){if(b.pendingRemoveContactIds.length){var c=b.pendingRemoveContactIds[0];return b.members[c]}else if(a.pendingRemoveContactIds.length){return!1}return null},y=function(a,b){var c=a.pendingDeleteMessageIds.length,d=b.pendingDeleteMessageIds.length;if(d&&!c){return{show:!0,type:b.type,canDeleteMessagesForAllUsers:b.canDeleteMessagesForAllUsers}}else if(c&&!d){return{show:!1}}return null},z=function(a,b){if(!a.pendingDeleteConversation&&b.pendingDeleteConversation){return b.type}else if(a.pendingDeleteConversation&&!b.pendingDeleteConversation){return!1}return null},A=function(a,b){var c=a.loggedInUserId,d=I(a),e=I(b),f=!d?[]:d.contactrequests.filter(function(a){return a.requesteduserid==c&&a.userid==d.id}),g=!e?[]:e.contactrequests.filter(function(a){return a.requesteduserid==c&&a.userid==e.id}),h=f.length?f[0]:null,i=g.length?g[0]:null;if(!h&&i){return e}else if(h&&!i){return!1}else{return null}},B=function(a,b){var c=I(a),d=I(b);if(!c&&!d){return null}else if(!c&&d){return d.isblocked?!0:null}else if(!d&&c){return c.isblocked?!1:null}else if(c.isblocked&&!d.isblocked){return!1}else if(!c.isblocked&&d.isblocked){return!0}else{return null}},C=function(a,b){var c=a.isFavourite,d=b.isFavourite;if(null===a.id&&null===b.id){return null}else if(null===a.id&&null!==b.id){return"show-add"}else if(null!==a.id&&null===b.id){return"hide"}else if(c==d){return null}else if(!c&&d){return"show-remove"}else if(c&&!d){return"show-add"}else{return null}},D=function(a,b){var c=a.isMuted,d=b.isMuted;if(null===a.id&&null===b.id){return null}else if(null===a.id&&null!==b.id){return"show-mute"}else if(null!==a.id&&null===b.id){return"hide"}else if(c==d){return null}else if(!c&&d){return"show-unmute"}else if(c&&!d){return"show-mute"}else{return null}},E=function(a,b){var c=a.loggedInUserId,d=I(a),e=I(b),f=!d?[]:d.contactrequests.filter(function(a){return a.userid==c&&a.requesteduserid==d.id||a.userid==d.id&&a.requesteduserid==c}),g=!e?[]:e.contactrequests.filter(function(a){return a.userid==c&&a.requesteduserid==e.id||a.userid==e.id&&a.requesteduserid==c}),h=0e&&1>f){return!0}if(e!=f){return!1}return c.every(function(a,b){return a==d[b]})},h=function(c,a){var b=Object.keys(c),d=Object.keys(a);if(b.length!=d.length){return!1}return b.every(function(b){var d=c[b],e=a[b],f=_typeof(d),i=_typeof(e);f=null===d?"null":f;i=null===d?"null":i;f="object"===f&&Array.isArray(f)?"array":f;i="object"===i&&Array.isArray(i)?"array":i;if(f!==i){return!1}switch(f){case"object":return h(d,e);case"array":return g(d,e);default:return c[b]==a[b];}})},i=function(c,a){return h({id:c.id,state:c.sendState,text:c.text,timeCreated:c.timeCreated},{id:a.id,state:a.sendState,text:a.text,timeCreated:a.timeCreated})},j=function(a,b,c){return{remove:b,add:c.map(function(b){var c=f(a,function(a){return b.timestampd.length){return null}if(1>c.length){return d[d.length-1].id}var e=c[a.messages.length-1],f=d[d.length-1],g=c[0],h=d[0];if(e.id!=f.id){return f.id}else if(g.id!=h.id){return g.id}return null},q=function(a,b){if(!a.loadingMembers&&b.loadingMembers){return!0}else if(a.loadingMembers&&!b.loadingMembers){return!1}else{return null}},r=function(a,b){if(a.hasTriedToLoadMessages===b.hasTriedToLoadMessages){return null}else if(!b.hasTriedToLoadMessages&&b.loadingMessages){return!0}else if(b.hasTriedToLoadMessages&&!b.loadingMessages){return!1}else{return null}},s=function(a,b){if(!a.loadingMessages&&b.loadingMessages){return!0}else if(a.loadingMessages&&!b.loadingMessages){return!1}else{return null}},t=function(a,b){if(!a.showEmojiPicker&&b.showEmojiPicker){return!0}else if(a.showEmojiPicker&&!b.showEmojiPicker){return!1}else{return null}},u=function(a,b){if(!a.showEmojiAutoComplete&&b.showEmojiAutoComplete){return!0}else if(a.showEmojiAutoComplete&&!b.showEmojiAutoComplete){return!1}else{return null}},v=function(a,b){if(b.pendingBlockUserIds.length){var c=b.pendingBlockUserIds[0];return b.members[c]}else if(a.pendingBlockUserIds.length){return!1}return null},w=function(a,b){if(b.pendingUnblockUserIds.length){var c=b.pendingUnblockUserIds[0];return b.members[c]}else if(a.pendingUnblockUserIds.length){return!1}return null},x=function(a,b){if(b.pendingAddContactIds.length){var c=b.pendingAddContactIds[0];return b.members[c]}else if(a.pendingAddContactIds.length){return!1}return null},y=function(a,b){if(b.pendingRemoveContactIds.length){var c=b.pendingRemoveContactIds[0];return b.members[c]}else if(a.pendingRemoveContactIds.length){return!1}return null},z=function(a,b){var c=a.pendingDeleteMessageIds.length,d=b.pendingDeleteMessageIds.length;if(d&&!c){return{show:!0,type:b.type,canDeleteMessagesForAllUsers:b.canDeleteMessagesForAllUsers}}else if(c&&!d){return{show:!1}}return null},A=function(a,b){if(!a.pendingDeleteConversation&&b.pendingDeleteConversation){return b.type}else if(a.pendingDeleteConversation&&!b.pendingDeleteConversation){return!1}return null},B=function(a,b){var c=a.loggedInUserId,d=J(a),e=J(b),f=!d?[]:d.contactrequests.filter(function(a){return a.requesteduserid==c&&a.userid==d.id}),g=!e?[]:e.contactrequests.filter(function(a){return a.requesteduserid==c&&a.userid==e.id}),h=f.length?f[0]:null,i=g.length?g[0]:null;if(!h&&i){return e}else if(h&&!i){return!1}else{return null}},C=function(a,b){var c=J(a),d=J(b);if(!c&&!d){return null}else if(!c&&d){return d.isblocked?!0:null}else if(!d&&c){return c.isblocked?!1:null}else if(c.isblocked&&!d.isblocked){return!1}else if(!c.isblocked&&d.isblocked){return!0}else{return null}},D=function(a,b){var c=a.isFavourite,d=b.isFavourite;if(null===a.id&&null===b.id){return null}else if(null===a.id&&null!==b.id){return"show-add"}else if(null!==a.id&&null===b.id){return"hide"}else if(c==d){return null}else if(!c&&d){return"show-remove"}else if(c&&!d){return"show-add"}else{return null}},E=function(a,b){var c=a.isMuted,d=b.isMuted;if(null===a.id&&null===b.id){return null}else if(null===a.id&&null!==b.id){return"show-mute"}else if(null!==a.id&&null===b.id){return"hide"}else if(c==d){return null}else if(!c&&d){return"show-unmute"}else if(c&&!d){return"show-mute"}else{return null}},F=function(a,b){var c=a.loggedInUserId,d=J(a),e=J(b),f=!d?[]:d.contactrequests.filter(function(a){return a.userid==c&&a.requesteduserid==d.id||a.userid==d.id&&a.requesteduserid==c}),g=!e?[]:e.contactrequests.filter(function(a){return a.userid==c&&a.requesteduserid==e.id||a.userid==e.id&&a.requesteduserid==c}),h=0.\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 * @return {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 }\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 * 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 and there are no messages in the\n * conversation.\n *\n * The messages should be hidden when there are messages in the conversation.\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 var hadMessages = state.messages.length > 0;\n var hasMessages = state.messages.length > 0;\n\n if (!oldRequest && newRequest && !newOtherUser.iscontact && !hasMessages) {\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 if (!hadMessages && hasMessages) {\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 }\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"],"file":"message_drawer_view_conversation_patcher.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/message_drawer_view_conversation_patcher.js"],"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","missingFromA","missingFromB","matches","forEach","current","found","index","length","next","splice","findPositionInArray","array","breakFunction","before","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","value","buildMessagesPatch","matchingDays","update","days","dayCurrent","dayNext","messagesDiff","patch","timeAdded","concat","after","buildConversationPatch","newState","diff","daysDiff","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","buildHeaderPatchTypeSelf","SELF","buildHeaderPatchTypePublic","oldMemberCount","newMemberCount","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","contactRequests","hasSentContactRequest","requirescontact","hadMessages","prevRequiresContactRequest","nextRequiresContactRequest","confirmAddContact","buildRequireUnblock","buildUnableToMessage","buildFooterPatchTypePrivate","loadingFirstMessages","inEditMode","requireUnblock","unableToMessage","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","confirmRemoveContact","confirmDeleteConversation","isBlocked","isContact","contactRequestSent","selfConversationMessage","patchConfig","extend","buildFunc"],"mappings":"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","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 * @return {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 }\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 and there are no messages in the\n * conversation.\n *\n * The messages should be hidden when there are messages in the conversation.\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 var hadMessages = state.messages.length > 0;\n var hasMessages = state.messages.length > 0;\n\n if (!oldRequest && newRequest && !newOtherUser.iscontact && !hasMessages) {\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 if (!hadMessages && hasMessages) {\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"],"file":"message_drawer_view_conversation_patcher.min.js"} \ No newline at end of file diff --git a/message/amd/build/message_drawer_view_conversation_renderer.min.js b/message/amd/build/message_drawer_view_conversation_renderer.min.js index 783198d1bdb..b24dd692fff 100644 --- a/message/amd/build/message_drawer_view_conversation_renderer.min.js +++ b/message/amd/build/message_drawer_view_conversation_renderer.min.js @@ -1,2 +1,2 @@ -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"],function(a,b,c,d,e,f){var g=f.SELECTORS,h=f.TEMPLATES,i=f.CONVERSATION_TYPES,j=function(a){return a.find(g.CONTENT_MESSAGES_CONTAINER)},k=function(a){j(a).removeClass("hidden")},l=function(a){j(a).addClass("hidden")},m=function(a){return a.find(g.SELF_CONVERSATION_MESSAGE_CONTAINER)},n=function(a){return m(a).addClass("hidden")},o=function(a){return a.find(g.CONTACT_REQUEST_SENT_MESSAGE_CONTAINER)},p=function(a){return o(a).addClass("hidden")},q=function(a){return a.find(g.CONTENT_MESSAGES_FOOTER_CONTAINER)},r=function(a){q(a).removeClass("hidden")},s=function(a){q(a).addClass("hidden")},t=function(a){return a.find(g.CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER)},u=function(a){t(a).removeClass("hidden")},v=function(a){t(a).addClass("hidden")},w=function(a){return a.find(g.PLACEHOLDER_CONTAINER)},x=function(a){w(a).removeClass("hidden")},y=function(a){w(a).addClass("hidden")},z=function(a){return a.find(g.CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER)},A=function(a){z(a).removeClass("hidden")},B=function(a){z(a).addClass("hidden")},C=function(a){return a.find(g.CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER)},D=function(a){C(a).removeClass("hidden")},E=function(a){C(a).addClass("hidden")},F=function(a){return a.find(g.CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER)},G=function(a){F(a).removeClass("hidden")},H=function(a){F(a).addClass("hidden")},I=function(a){P(a);S(a);V(a)},J=function(a){s(a);v(a);y(a);B(a);E(a);H(a)},K=function(a){return a.find(g.CONTENT_PLACEHOLDER_CONTAINER)},L=function(a){K(a).removeClass("hidden")},M=function(a){K(a).addClass("hidden")},N=function(a){return a.find(g.HEADER)},O=function(a){N(a).removeClass("hidden")},P=function(a){N(a).addClass("hidden")},Q=function(a){return a.find(g.HEADER_EDIT_MODE)},R=function(a){Q(a).removeClass("hidden")},S=function(a){Q(a).addClass("hidden")},T=function(a){return a.find(g.HEADER_PLACEHOLDER_CONTAINER)},U=function(a){T(a).removeClass("hidden")},V=function(a){T(a).addClass("hidden")},W=function(a){return a.find(g.EMOJI_PICKER_CONTAINER)},X=function(a,b){var c=j(a);return c.find("[data-message-id=\""+b+"\"]")},Y=function(a,b){var c=j(a);return c.find("[data-day-id=\""+b+"\"]")},Z=function(a){return a.find(g.MORE_MESSAGES_LOADING_ICON_CONTAINER)},$=function(a){Z(a).removeClass("hidden")},_=function(a){Z(a).addClass("hidden")},aa=function(a){return a.find(g.CONFIRM_DIALOGUE_CONTAINER)},ba=function(a){var b=aa(a),c=b.siblings(":not(.hidden)");c.attr("aria-hidden",!0);c.attr("tabindex",-1);c.attr("data-confirm-dialogue-hidden",!0);b.removeClass("hidden")},ca=function(a){var b=aa(a),c=b.siblings("[data-confirm-dialogue-hidden=\"true\"]");c.removeAttr("aria-hidden");c.removeAttr("tabindex");c.removeAttr("data-confirm-dialogue-hidden");b.addClass("hidden")},da=function(a,b){Q(a).find(g.MESSAGES_SELECTED_COUNT).text(b)},ea=function(a,b){return a.map(function(a){return{id:a.id,isread:a.isRead,fromloggedinuser:a.fromLoggedInUser,userfrom:a.userFrom,text:a.text,formattedtime:a.timeCreated?b[a.timeCreated]:null}})},fa=function(b,c,e,f,g){var i=j(c),k=f.map(function(a){return d.render(h.DAY,{timestamp:a.value.timestamp,messages:ea(a.value.messages,g)})});return a.when.apply(a,k).then(function(){f.forEach(function(b,d){k[d].then(function(d){if(b.before){var e=Y(c,b.before.timestamp);return a(d).insertBefore(e)}else{return i.append(d)}}).catch(function(){})})})},ga=function(b,c,e,f,i){var j=f.map(function(a){return a.value}),k=ea(j,i);return d.render(h.MESSAGES,{messages:k}).then(function(b){var d=a(b);f.forEach(function(a){var b=d.find("[data-message-id=\""+a.value.id+"\"]");if(a.before){var e=X(c,a.before.id);return b.insertBefore(e)}else{var f=Y(c,a.day.timestamp),h=f.find(g.DAY_MESSAGES_CONTAINER);return h.append(b)}})})},ha=function(a,b,c,d,e){d.forEach(function(a){var c=a.before,d=a.after,f=X(b,c.id);if(c.id!=d.id){f.attr("data-message-id",d.id)}if(c.timeCreated!=d.timeCreated){var h=e[d.timeCreated];f.find(g.LOADING_ICON_CONTAINER).addClass("hidden");f.find(g.TIME_CREATED).text(h).removeClass("hidden")}if(c.sendState!=d.sendState){var i=f.find(g.LOADING_ICON_CONTAINER),j=f.find(g.TIME_CREATED),k=f.find(g.RETRY_SEND);i.addClass("hidden");i.attr("aria-hidden","true");j.addClass("hidden");j.attr("aria-hidden","true");k.addClass("hidden");k.attr("aria-hidden","true");f.removeClass("border border-danger");switch(d.sendState){case"pending":i.removeClass("hidden");i.attr("aria-hidden","false");break;case"error":k.removeClass("hidden");k.attr("aria-hidden","false");f.addClass("border border-danger");break;case"sent":j.removeClass("hidden");j.attr("aria-hidden","false");break;}}if(c.text!=d.text){f.find(g.TEXT_CONTAINER).html(d.text)}if(c.errorMessage!=d.errorMessage){var l=f.find(g.ERROR_MESSAGE_CONTAINER),a=l.find(g.ERROR_MESSAGE);if(d.errorMessage){l.removeClass("hidden");l.attr("aria-hidden","false");a.text(d.errorMessage)}else{l.addClass("hidden");l.attr("aria-hidden","true");a.text("")}}})},ia=function(a,b){b.forEach(function(b){Y(a,b.timestamp).remove()})},ja=function(a,b){b.forEach(function(b){X(a,b.id).remove()})},ka=function(b,d,f,g){var h=[],i=0.\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],\nfunction(\n $,\n Notification,\n Str,\n Templates,\n UserDate,\n Constants\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 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 siblings.attr('aria-hidden', true);\n siblings.attr('tabindex', -1);\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 siblings.removeAttr('aria-hidden');\n siblings.removeAttr('tabindex');\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 return Templates.render(TEMPLATES.DAY, {\n timestamp: data.value.timestamp,\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 loading.attr('aria-hidden', 'true');\n time.addClass('hidden');\n time.attr('aria-hidden', 'true');\n retry.addClass('hidden');\n retry.attr('aria-hidden', 'true');\n element.removeClass('border border-danger');\n\n switch (after.sendState) {\n case 'pending':\n loading.removeClass('hidden');\n loading.attr('aria-hidden', 'false');\n break;\n case 'error':\n retry.removeClass('hidden');\n retry.attr('aria-hidden', 'false');\n element.addClass('border border-danger');\n break;\n case 'sent':\n time.removeClass('hidden');\n time.attr('aria-hidden', 'false');\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 messageContainer.attr('aria-hidden', 'false');\n message.text(after.errorMessage);\n } else {\n messageContainer.addClass('hidden');\n messageContainer.attr('aria-hidden', 'true');\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 container.attr('aria-hidden', false);\n container.find(SELECTORS.EMOJI_PICKER_SEARCH_INPUT).focus();\n } else {\n container.addClass('hidden');\n container.attr('aria-hidden', true);\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 dialogueHeader.removeClass('hidden');\n dialogueHeader.text(headerText);\n } else {\n dialogueHeader.addClass('hidden');\n dialogueHeader.text('');\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 dialogueHeader.addClass('hidden');\n dialogueHeader.text('');\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 if (user.canmessageevenifblocked) {\n return Str.get_string('cantblockuser', 'core_message', user.fullname)\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', user.fullname)\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', 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 return Str.get_string('addcontactconfirm', 'core_message', user.fullname)\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', 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 {Bool} 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', 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} isFavourite 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 {Object} 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', 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 },\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 return {\n render: render,\n };\n});\n"],"file":"message_drawer_view_conversation_renderer.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/message_drawer_view_conversation_renderer.js"],"names":["define","$","Notification","Str","Templates","UserDate","Constants","SELECTORS","TEMPLATES","CONVERSATION_TYPES","getMessagesContainer","body","find","CONTENT_MESSAGES_CONTAINER","showMessagesContainer","removeClass","hideMessagesContainer","addClass","getSelfConversationMessageContainer","SELF_CONVERSATION_MESSAGE_CONTAINER","hideSelfConversationMessageContainer","getContactRequestSentContainer","CONTACT_REQUEST_SENT_MESSAGE_CONTAINER","hideContactRequestSentContainer","getFooterContentContainer","footer","CONTENT_MESSAGES_FOOTER_CONTAINER","showFooterContent","hideFooterContent","getFooterEditModeContainer","CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER","showFooterEditMode","hideFooterEditMode","getFooterPlaceholderContainer","PLACEHOLDER_CONTAINER","showFooterPlaceholder","hideFooterPlaceholder","getFooterRequireContactContainer","CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER","showFooterRequireContact","hideFooterRequireContact","getFooterRequireUnblockContainer","CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER","showFooterRequireUnblock","hideFooterRequireUnblock","getFooterUnableToMessageContainer","CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER","showFooterUnableToMessage","hideFooterUnableToMessage","hideAllHeaderElements","header","hideHeaderContent","hideHeaderEditMode","hideHeaderPlaceholder","hideAllFooterElements","getContentPlaceholderContainer","CONTENT_PLACEHOLDER_CONTAINER","showContentPlaceholder","hideContentPlaceholder","getHeaderContent","HEADER","showHeaderContent","getHeaderEditMode","HEADER_EDIT_MODE","showHeaderEditMode","getHeaderPlaceholderContainer","HEADER_PLACEHOLDER_CONTAINER","showHeaderPlaceholder","getEmojiPickerContainer","EMOJI_PICKER_CONTAINER","getEmojiAutoCompleteContainer","EMOJI_AUTO_COMPLETE_CONTAINER","getMessageElement","messageId","messagesContainer","getDayElement","dayTimeCreated","getMoreMessagesLoadingIconContainer","MORE_MESSAGES_LOADING_ICON_CONTAINER","showMoreMessagesLoadingIcon","hideMoreMessagesLoadingIcon","getConfirmDialogueContainer","root","CONFIRM_DIALOGUE_CONTAINER","showConfirmDialogueContainer","container","siblings","attr","hideConfirmDialogueContainer","removeAttr","setMessagesSelectedCount","value","MESSAGES_SELECTED_COUNT","text","formatMessagesForTemplate","messages","datesCache","map","message","id","isread","isRead","fromloggedinuser","fromLoggedInUser","userfrom","userFrom","formattedtime","timeCreated","renderAddDays","days","daysRenderPromises","data","render","DAY","timestamp","when","apply","then","forEach","index","html","before","element","insertBefore","append","catch","renderAddMessages","messagesData","formattedMessages","MESSAGES","messageList","messageHtml","dayContainer","day","dayMessagesContainer","DAY_MESSAGES_CONTAINER","renderUpdateMessages","after","formattedTime","LOADING_ICON_CONTAINER","TIME_CREATED","sendState","loading","time","retry","RETRY_SEND","TEXT_CONTAINER","errorMessage","messageContainer","ERROR_MESSAGE_CONTAINER","ERROR_MESSAGE","renderRemoveDays","remove","renderRemoveMessages","renderConversation","renderingPromises","hasAddDays","add","length","hasAddMessages","hasUpdateMessages","update","timestampsToFormat","datesCachePromise","Deferred","resolve","promise","concat","reduce","carry","timestamps","push","get_string","format","requests","get","formattedTimes","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","renderScrollToMessage","messageElement","position","scrollTop","top","renderLoadingMembers","isLoadingMembers","renderLoadingFirstMessages","isLoadingFirstMessages","renderLoadingMessages","isLoading","renderShowEmojiPicker","show","EMOJI_PICKER_SEARCH_INPUT","focus","renderShowEmojiAutoComplete","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","button","CAN_RECEIVE_FOCUS","filter","first","hideConfirmDialogue","hideCheckDeleteDialogue","renderConfirmBlockUser","canmessageevenifblocked","string","ACTION_CONFIRM_BLOCK","renderConfirmUnblockUser","ACTION_CONFIRM_UNBLOCK","renderConfirmAddContact","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","checkboxRegion","DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER","checkbox","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","MESSAGE_SELECTED_ICON","renderSelectedMessages","hasSelectedMessages","count","renderRequireAddContact","hasMessages","renderSelfConversationMessage","displayMessage","renderContactRequestSent","userFullName","renderReset","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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","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],\nfunction(\n $,\n Notification,\n Str,\n Templates,\n UserDate,\n Constants\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 siblings.attr('aria-hidden', true);\n siblings.attr('tabindex', -1);\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 siblings.removeAttr('aria-hidden');\n siblings.removeAttr('tabindex');\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 return Templates.render(TEMPLATES.DAY, {\n timestamp: data.value.timestamp,\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 loading.attr('aria-hidden', 'true');\n time.addClass('hidden');\n time.attr('aria-hidden', 'true');\n retry.addClass('hidden');\n retry.attr('aria-hidden', 'true');\n element.removeClass('border border-danger');\n\n switch (after.sendState) {\n case 'pending':\n loading.removeClass('hidden');\n loading.attr('aria-hidden', 'false');\n break;\n case 'error':\n retry.removeClass('hidden');\n retry.attr('aria-hidden', 'false');\n element.addClass('border border-danger');\n break;\n case 'sent':\n time.removeClass('hidden');\n time.attr('aria-hidden', 'false');\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 messageContainer.attr('aria-hidden', 'false');\n message.text(after.errorMessage);\n } else {\n messageContainer.addClass('hidden');\n messageContainer.attr('aria-hidden', 'true');\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 container.attr('aria-hidden', false);\n container.find(SELECTORS.EMOJI_PICKER_SEARCH_INPUT).focus();\n } else {\n container.addClass('hidden');\n container.attr('aria-hidden', true);\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 container.attr('aria-hidden', false);\n } else {\n container.addClass('hidden');\n container.attr('aria-hidden', true);\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 dialogueHeader.removeClass('hidden');\n dialogueHeader.text(headerText);\n } else {\n dialogueHeader.addClass('hidden');\n dialogueHeader.text('');\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 dialogueHeader.addClass('hidden');\n dialogueHeader.text('');\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 if (user.canmessageevenifblocked) {\n return Str.get_string('cantblockuser', 'core_message', user.fullname)\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', user.fullname)\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', 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 return Str.get_string('addcontactconfirm', 'core_message', user.fullname)\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', 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 {Bool} 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', 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} isFavourite 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 {Object} 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', 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 return {\n render: render,\n };\n});\n"],"file":"message_drawer_view_conversation_renderer.min.js"} \ No newline at end of file diff --git a/message/amd/build/message_drawer_view_conversation_state_manager.min.js b/message/amd/build/message_drawer_view_conversation_state_manager.min.js index 67b196bf1a0..71db5bebb1b 100644 --- a/message/amd/build/message_drawer_view_conversation_state_manager.min.js +++ b/message/amd/build/message_drawer_view_conversation_state_manager.min.js @@ -1,2 +1,2 @@ -define ("core_message/message_drawer_view_conversation_state_manager",["jquery"],function(a){var b=function(b){return a.extend(!0,{},b)},c=function(a,b,c){return a.map(function(a){var d=a.useridfrom==b;return{id:""+a.id,fromLoggedInUser:d,userFrom:c[a.useridfrom],text:a.text,timeCreated:a.timecreated?parseInt(a.timecreated,10):null}})},d=function(a){return a.map(function(a){return{id:a.id,fullname:a.fullname,profileurl:a.profileurl,profileimageurl:a.profileimageurl,profileimageurlsmall:a.profileimageurlsmall,isonline:a.isonline,showonlinestatus:a.showonlinestatus,isblocked:a.isblocked,iscontact:a.iscontact,isdeleted:a.isdeleted,canmessage:a.canmessage,canmessageevenifblocked:a.canmessageevenifblocked,requirescontact:a.requirescontact,contactrequests:a.contactrequests||[]}})};return{buildInitialState:function buildInitialState(a,b,c,d,e,f){return{midnight:a,loggedInUserId:b,id:c,messagePollMin:d,messagePollMax:e,messagePollAfterMax:f,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:[],showEmojiPicker:!1}},addMessages:function addMessages(a,d){var e=b(a),f=c(d,a.loggedInUserId,a.members);f=f.map(function(a){a.sendState=null;a.timeAdded=Date.now();a.errorMessage=null;return a});var g=a.messages.concat(f);g.sort(function(c,a){if(null===c.timeCreated&&null===a.timeCreated){if(c.timeAddeda.timeAdded){return 1}}if(null===c.timeCreated&&null!==a.timeCreated){return 1}else if(null!==c.timeCreated&&null===a.timeCreated){return-1}else if(c.timeCreateda.timeCreated){return 1}else if(c.ida.id){return 1}else{return 0}});e.messages=g.filter(function(a,b,c){return!b||a.id!=c[b-1].id});return e},updateMessages:function updateMessages(d,e){var f=b(d),g=e.reduce(function(a,b){var e=b[0],f=b[1],g=c([f],d.loggedInUserId,d.members),h=g[0];a[e.id]=h;return a},{});f.messages=f.messages.map(function(b){if(b.id in g){return a.extend(b,g[b.id])}else{return b}});return f},removeMessages:function removeMessages(a,c){var d=b(a),e=c.map(function(a){return""+a.id});d.messages=d.messages.filter(function(a){return 0>e.indexOf(a.id)});return d},removeMessagesById:function removeMessagesById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.messages=d.messages.filter(function(a){return 0>c.indexOf(a.id)});return d},addMembers:function addMembers(a,c){var e=b(a),f=d(c);f.forEach(function(a){e.members[a.id]=a});return e},removeMembers:function removeMembers(a,c){var d=b(a);c.forEach(function(a){delete d.members[a.id]});return d},setLoadingMessages:function setLoadingMessages(a,c){var d=b(a);d.loadingMessages=c;if(a.loadingMessages&&!c){d.hasTriedToLoadMessages=!0}return d},setLoadingMembers:function setLoadingMembers(a,c){var d=b(a);d.loadingMembers=c;return d},setId:function setId(a,c){var d=b(a);d.id=c;return d},setName:function setName(a,c){var d=b(a);d.name=c;return d},setSubname:function setSubname(a,c){var d=b(a);d.subname=c;return d},setType:function setType(a,c){var d=b(a);d.type=c;return d},setIsFavourite:function setIsFavourite(a,c){var d=b(a);d.isFavourite=c;return d},setIsMuted:function setIsMuted(a,c){var d=b(a);d.isMuted=c;return d},setCanDeleteMessagesForAllUsers:function setCanDeleteMessagesForAllUsers(a,c){var d=b(a);d.canDeleteMessagesForAllUsers=c;return d},setDeleteMessagesForAllUsers:function setDeleteMessagesForAllUsers(a,c){var d=b(a);d.deleteMessagesForAllUsers=c;return d},setTotalMemberCount:function setTotalMemberCount(a,c){var d=b(a);d.totalMemberCount=c;return d},setImageUrl:function setImageUrl(a,c){var d=b(a);d.imageUrl=c;return d},setLoadingConfirmAction:function setLoadingConfirmAction(a,c){var d=b(a);d.loadingConfirmAction=c;return d},setPendingDeleteConversation:function setPendingDeleteConversation(a,c){var d=b(a);d.pendingDeleteConversation=c;return d},setMessagesSendPendingById:function setMessagesSendPendingById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.messages.forEach(function(a){if(0<=c.indexOf(a.id)){a.sendState="pending";a.errorMessage=null}});return d},setMessagesSendSuccessById:function setMessagesSendSuccessById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.messages.forEach(function(a){if(0<=c.indexOf(a.id)){a.sendState="sent";a.errorMessage=null}});return d},setMessagesSendFailById:function setMessagesSendFailById(a,c,d){var e=b(a);c=c.map(function(a){return""+a});e.messages.forEach(function(a){if(0<=c.indexOf(a.id)){a.sendState="error";a.errorMessage=d}});return e},setShowEmojiPicker:function setShowEmojiPicker(a,c){var d=b(a);d.showEmojiPicker=c;return d},addPendingBlockUsersById:function addPendingBlockUsersById(a,c){var d=b(a);c.forEach(function(a){d.pendingBlockUserIds.push(a)});return d},addPendingRemoveContactsById:function addPendingRemoveContactsById(a,c){var d=b(a);c.forEach(function(a){d.pendingRemoveContactIds.push(a)});return d},addPendingUnblockUsersById:function addPendingUnblockUsersById(a,c){var d=b(a);c.forEach(function(a){d.pendingUnblockUserIds.push(a)});return d},addPendingAddContactsById:function addPendingAddContactsById(a,c){var d=b(a);c.forEach(function(a){d.pendingAddContactIds.push(a)});return d},addPendingDeleteMessagesById:function addPendingDeleteMessagesById(a,c){var d=b(a);c.forEach(function(a){d.pendingDeleteMessageIds.push(a)});return d},removePendingBlockUsersById:function removePendingBlockUsersById(a,c){var d=b(a);d.pendingBlockUserIds=d.pendingBlockUserIds.filter(function(a){return 0>c.indexOf(a)});return d},removePendingRemoveContactsById:function removePendingRemoveContactsById(a,c){var d=b(a);d.pendingRemoveContactIds=d.pendingRemoveContactIds.filter(function(a){return 0>c.indexOf(a)});return d},removePendingUnblockUsersById:function removePendingUnblockUsersById(a,c){var d=b(a);d.pendingUnblockUserIds=d.pendingUnblockUserIds.filter(function(a){return 0>c.indexOf(a)});return d},removePendingAddContactsById:function removePendingAddContactsById(a,c){var d=b(a);d.pendingAddContactIds=d.pendingAddContactIds.filter(function(a){return 0>c.indexOf(a)});return d},removePendingDeleteMessagesById:function removePendingDeleteMessagesById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.pendingDeleteMessageIds=d.pendingDeleteMessageIds.filter(function(a){return 0>c.indexOf(a)});return d},addSelectedMessagesById:function addSelectedMessagesById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.selectedMessageIds=d.selectedMessageIds.concat(c);return d},removeSelectedMessagesById:function removeSelectedMessagesById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.selectedMessageIds=d.selectedMessageIds.filter(function(a){return 0>c.indexOf(a)});return d},markMessagesAsRead:function markMessagesAsRead(a,c){var d=b(a),e=c.map(function(a){return a.id});d.messages=d.messages.map(function(a){if(0<=e.indexOf(a.id)){a.isRead=!0}return a});return d},addContactRequests:function addContactRequests(a,c){var d=b(a);c.forEach(function(a){var b=a.userid,c=a.requesteduserid;d.members[b].contactrequests.push(a);d.members[c].contactrequests.push(a)});return d},removeContactRequests:function removeContactRequests(a,c){var d=b(a);c.forEach(function(a){var b=a.userid,c=a.requesteduserid;d.members[b].contactrequests=d.members[b].contactrequests.filter(function(a){return a.userid!=b});d.members[c].contactrequests=d.members[c].contactrequests.filter(function(a){return a.requesteduserid!=c})});return d}}}); +define ("core_message/message_drawer_view_conversation_state_manager",["jquery"],function(a){var b=function(b){return a.extend(!0,{},b)},c=function(a,b,c){return a.map(function(a){var d=a.useridfrom==b;return{id:""+a.id,fromLoggedInUser:d,userFrom:c[a.useridfrom],text:a.text,timeCreated:a.timecreated?parseInt(a.timecreated,10):null}})},d=function(a){return a.map(function(a){return{id:a.id,fullname:a.fullname,profileurl:a.profileurl,profileimageurl:a.profileimageurl,profileimageurlsmall:a.profileimageurlsmall,isonline:a.isonline,showonlinestatus:a.showonlinestatus,isblocked:a.isblocked,iscontact:a.iscontact,isdeleted:a.isdeleted,canmessage:a.canmessage,canmessageevenifblocked:a.canmessageevenifblocked,requirescontact:a.requirescontact,contactrequests:a.contactrequests||[]}})};return{buildInitialState:function buildInitialState(a,b,c,d,e,f){return{midnight:a,loggedInUserId:b,id:c,messagePollMin:d,messagePollMax:e,messagePollAfterMax:f,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 addMessages(a,d){var e=b(a),f=c(d,a.loggedInUserId,a.members);f=f.map(function(a){a.sendState=null;a.timeAdded=Date.now();a.errorMessage=null;return a});var g=a.messages.concat(f);g.sort(function(c,a){if(null===c.timeCreated&&null===a.timeCreated){if(c.timeAddeda.timeAdded){return 1}}if(null===c.timeCreated&&null!==a.timeCreated){return 1}else if(null!==c.timeCreated&&null===a.timeCreated){return-1}else if(c.timeCreateda.timeCreated){return 1}else if(c.ida.id){return 1}else{return 0}});e.messages=g.filter(function(a,b,c){return!b||a.id!=c[b-1].id});return e},updateMessages:function updateMessages(d,e){var f=b(d),g=e.reduce(function(a,b){var e=b[0],f=b[1],g=c([f],d.loggedInUserId,d.members),h=g[0];a[e.id]=h;return a},{});f.messages=f.messages.map(function(b){if(b.id in g){return a.extend(b,g[b.id])}else{return b}});return f},removeMessages:function removeMessages(a,c){var d=b(a),e=c.map(function(a){return""+a.id});d.messages=d.messages.filter(function(a){return 0>e.indexOf(a.id)});return d},removeMessagesById:function removeMessagesById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.messages=d.messages.filter(function(a){return 0>c.indexOf(a.id)});return d},addMembers:function addMembers(a,c){var e=b(a),f=d(c);f.forEach(function(a){e.members[a.id]=a});return e},removeMembers:function removeMembers(a,c){var d=b(a);c.forEach(function(a){delete d.members[a.id]});return d},setLoadingMessages:function setLoadingMessages(a,c){var d=b(a);d.loadingMessages=c;if(a.loadingMessages&&!c){d.hasTriedToLoadMessages=!0}return d},setLoadingMembers:function setLoadingMembers(a,c){var d=b(a);d.loadingMembers=c;return d},setId:function setId(a,c){var d=b(a);d.id=c;return d},setName:function setName(a,c){var d=b(a);d.name=c;return d},setSubname:function setSubname(a,c){var d=b(a);d.subname=c;return d},setType:function setType(a,c){var d=b(a);d.type=c;return d},setIsFavourite:function setIsFavourite(a,c){var d=b(a);d.isFavourite=c;return d},setIsMuted:function setIsMuted(a,c){var d=b(a);d.isMuted=c;return d},setCanDeleteMessagesForAllUsers:function setCanDeleteMessagesForAllUsers(a,c){var d=b(a);d.canDeleteMessagesForAllUsers=c;return d},setDeleteMessagesForAllUsers:function setDeleteMessagesForAllUsers(a,c){var d=b(a);d.deleteMessagesForAllUsers=c;return d},setTotalMemberCount:function setTotalMemberCount(a,c){var d=b(a);d.totalMemberCount=c;return d},setImageUrl:function setImageUrl(a,c){var d=b(a);d.imageUrl=c;return d},setLoadingConfirmAction:function setLoadingConfirmAction(a,c){var d=b(a);d.loadingConfirmAction=c;return d},setPendingDeleteConversation:function setPendingDeleteConversation(a,c){var d=b(a);d.pendingDeleteConversation=c;return d},setMessagesSendPendingById:function setMessagesSendPendingById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.messages.forEach(function(a){if(0<=c.indexOf(a.id)){a.sendState="pending";a.errorMessage=null}});return d},setMessagesSendSuccessById:function setMessagesSendSuccessById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.messages.forEach(function(a){if(0<=c.indexOf(a.id)){a.sendState="sent";a.errorMessage=null}});return d},setMessagesSendFailById:function setMessagesSendFailById(a,c,d){var e=b(a);c=c.map(function(a){return""+a});e.messages.forEach(function(a){if(0<=c.indexOf(a.id)){a.sendState="error";a.errorMessage=d}});return e},setShowEmojiAutoComplete:function setShowEmojiAutoComplete(a,c){var d=b(a);d.showEmojiAutoComplete=c;return d},setShowEmojiPicker:function setShowEmojiPicker(a,c){var d=b(a);d.showEmojiPicker=c;return d},addPendingBlockUsersById:function addPendingBlockUsersById(a,c){var d=b(a);c.forEach(function(a){d.pendingBlockUserIds.push(a)});return d},addPendingRemoveContactsById:function addPendingRemoveContactsById(a,c){var d=b(a);c.forEach(function(a){d.pendingRemoveContactIds.push(a)});return d},addPendingUnblockUsersById:function addPendingUnblockUsersById(a,c){var d=b(a);c.forEach(function(a){d.pendingUnblockUserIds.push(a)});return d},addPendingAddContactsById:function addPendingAddContactsById(a,c){var d=b(a);c.forEach(function(a){d.pendingAddContactIds.push(a)});return d},addPendingDeleteMessagesById:function addPendingDeleteMessagesById(a,c){var d=b(a);c.forEach(function(a){d.pendingDeleteMessageIds.push(a)});return d},removePendingBlockUsersById:function removePendingBlockUsersById(a,c){var d=b(a);d.pendingBlockUserIds=d.pendingBlockUserIds.filter(function(a){return 0>c.indexOf(a)});return d},removePendingRemoveContactsById:function removePendingRemoveContactsById(a,c){var d=b(a);d.pendingRemoveContactIds=d.pendingRemoveContactIds.filter(function(a){return 0>c.indexOf(a)});return d},removePendingUnblockUsersById:function removePendingUnblockUsersById(a,c){var d=b(a);d.pendingUnblockUserIds=d.pendingUnblockUserIds.filter(function(a){return 0>c.indexOf(a)});return d},removePendingAddContactsById:function removePendingAddContactsById(a,c){var d=b(a);d.pendingAddContactIds=d.pendingAddContactIds.filter(function(a){return 0>c.indexOf(a)});return d},removePendingDeleteMessagesById:function removePendingDeleteMessagesById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.pendingDeleteMessageIds=d.pendingDeleteMessageIds.filter(function(a){return 0>c.indexOf(a)});return d},addSelectedMessagesById:function addSelectedMessagesById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.selectedMessageIds=d.selectedMessageIds.concat(c);return d},removeSelectedMessagesById:function removeSelectedMessagesById(a,c){var d=b(a);c=c.map(function(a){return""+a});d.selectedMessageIds=d.selectedMessageIds.filter(function(a){return 0>c.indexOf(a)});return d},markMessagesAsRead:function markMessagesAsRead(a,c){var d=b(a),e=c.map(function(a){return a.id});d.messages=d.messages.map(function(a){if(0<=e.indexOf(a.id)){a.isRead=!0}return a});return d},addContactRequests:function addContactRequests(a,c){var d=b(a);c.forEach(function(a){var b=a.userid,c=a.requesteduserid;d.members[b].contactrequests.push(a);d.members[c].contactrequests.push(a)});return d},removeContactRequests:function removeContactRequests(a,c){var d=b(a);c.forEach(function(a){var b=a.userid,c=a.requesteduserid;d.members[b].contactrequests=d.members[b].contactrequests.filter(function(a){return a.userid!=b});d.members[c].contactrequests=d.members[c].contactrequests.filter(function(a){return a.requesteduserid!=c})});return d}}}); //# sourceMappingURL=message_drawer_view_conversation_state_manager.min.js.map diff --git a/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map b/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map index 833ea30c3d6..d848b36fb2d 100644 --- a/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map +++ b/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/message_drawer_view_conversation_state_manager.js"],"names":["define","$","cloneState","state","extend","formatMessages","messages","loggedInUserId","members","map","message","fromLoggedInUser","useridfrom","id","userFrom","text","timeCreated","timecreated","parseInt","formatMembers","member","fullname","profileurl","profileimageurl","profileimageurlsmall","isonline","showonlinestatus","isblocked","iscontact","isdeleted","canmessage","canmessageevenifblocked","requirescontact","contactrequests","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","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","forEach","removeMembers","setLoadingMessages","value","setLoadingMembers","setId","setName","setSubname","setType","setIsFavourite","setIsMuted","setCanDeleteMessagesForAllUsers","setDeleteMessagesForAllUsers","setTotalMemberCount","count","setImageUrl","url","setLoadingConfirmAction","setPendingDeleteConversation","setMessagesSendPendingById","setMessagesSendSuccessById","setMessagesSendFailById","setShowEmojiPicker","show","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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","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 };\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 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 * @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 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 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"],"file":"message_drawer_view_conversation_state_manager.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/message_drawer_view_conversation_state_manager.js"],"names":["define","$","cloneState","state","extend","formatMessages","messages","loggedInUserId","members","map","message","fromLoggedInUser","useridfrom","id","userFrom","text","timeCreated","timecreated","parseInt","formatMembers","member","fullname","profileurl","profileimageurl","profileimageurlsmall","isonline","showonlinestatus","isblocked","iscontact","isdeleted","canmessage","canmessageevenifblocked","requirescontact","contactrequests","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","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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","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 };\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 * @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"],"file":"message_drawer_view_conversation_state_manager.min.js"} \ No newline at end of file diff --git a/message/amd/src/message_drawer_view_conversation.js b/message/amd/src/message_drawer_view_conversation.js index 2f3b2b3525b..7e3c0da59f1 100644 --- a/message/amd/src/message_drawer_view_conversation.js +++ b/message/amd/src/message_drawer_view_conversation.js @@ -69,6 +69,7 @@ define( '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( @@ -87,6 +88,7 @@ function( StateManager, MessageDrawerRouter, MessageDrawerRoutes, + initialiseEmojiAutoComplete, initialiseEmojiPicker ) { @@ -1556,6 +1558,7 @@ function( var isLoadingMoreMessages = false; var messagesContainer = getMessagesContainer(body); var emojiPickerElement = footer.find(SELECTORS.EMOJI_PICKER); + var emojiAutoCompleteContainer = footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER); var messageTextArea = footer.find(SELECTORS.MESSAGE_TEXT_AREA); var headerActivateHandlers = [ [SELECTORS.ACTION_REQUEST_BLOCK, generateConfirmActionHandler(requestBlockUser)], @@ -1597,6 +1600,30 @@ function( AutoRows.init(footer); + initialiseEmojiAutoComplete( + emojiAutoCompleteContainer[0], + messageTextArea[0], + function(hasSuggestions) { + var newState = StateManager.setShowEmojiAutoComplete(viewState, hasSuggestions); + render(newState); + }, + function(emoji) { + var newState = StateManager.setShowEmojiAutoComplete(viewState, false); + render(newState); + + messageTextArea.focus(); + var cursorPos = messageTextArea.prop('selectionStart'); + var currentText = messageTextArea.val(); + var textBefore = currentText.substring(0, cursorPos).replace(/\S*$/, ''); + var textAfter = currentText.substring(cursorPos).replace(/^\S*/, ''); + + messageTextArea.val(textBefore + emoji + textAfter); + // Set the cursor position to after the inserted emoji. + messageTextArea.prop('selectionStart', textBefore.length + emoji.length); + messageTextArea.prop('selectionEnd', textBefore.length + emoji.length); + } + ); + initialiseEmojiPicker(emojiPickerElement[0], function(emoji) { var newState = StateManager.setShowEmojiPicker(viewState, !viewState.showEmojiPicker); render(newState); diff --git a/message/amd/src/message_drawer_view_conversation_constants.js b/message/amd/src/message_drawer_view_conversation_constants.js index 47c36ca8e0e..e22d008c879 100644 --- a/message/amd/src/message_drawer_view_conversation_constants.js +++ b/message/amd/src/message_drawer_view_conversation_constants.js @@ -65,6 +65,7 @@ define([], function() { DAY_MESSAGES_CONTAINER: '[data-region="day-messages-container"]', DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE: '[data-region="delete-messages-for-all-users-toggle"]', DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER: '[data-region="delete-messages-for-all-users-toggle-container"]', + 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"]', diff --git a/message/amd/src/message_drawer_view_conversation_patcher.js b/message/amd/src/message_drawer_view_conversation_patcher.js index e8029a73108..c969b685577 100644 --- a/message/amd/src/message_drawer_view_conversation_patcher.js +++ b/message/amd/src/message_drawer_view_conversation_patcher.js @@ -601,6 +601,23 @@ function( } }; + /** + * Determine if we should show the emoji auto complete. + * + * @param {Object} state The current state. + * @param {Object} newState The new state. + * @return {Bool|Null} + */ + var buildShowEmojiAutoComplete = function(state, newState) { + if (!state.showEmojiAutoComplete && newState.showEmojiAutoComplete) { + return true; + } else if (state.showEmojiAutoComplete && !newState.showEmojiAutoComplete) { + return false; + } else { + return null; + } + }; + /** * Get the user Object of user to be blocked if pending. * @@ -1353,7 +1370,8 @@ function( selectedMessages: buildSelectedMessages, isFavourite: buildIsFavourite, isMuted: buildIsMuted, - showEmojiPicker: buildShowEmojiPicker + showEmojiPicker: buildShowEmojiPicker, + showEmojiAutoComplete: buildShowEmojiAutoComplete } }; // These build functions are only applicable to private conversations. diff --git a/message/amd/src/message_drawer_view_conversation_renderer.js b/message/amd/src/message_drawer_view_conversation_renderer.js index 226d63c88fc..dbbb8713165 100644 --- a/message/amd/src/message_drawer_view_conversation_renderer.js +++ b/message/amd/src/message_drawer_view_conversation_renderer.js @@ -430,6 +430,16 @@ function( return footer.find(SELECTORS.EMOJI_PICKER_CONTAINER); }; + /** + * Get the emoji picker container element. + * + * @param {Object} footer Conversation footer container element. + * @return {Object} The emoji picker container element. + */ + var getEmojiAutoCompleteContainer = function(footer) { + return footer.find(SELECTORS.EMOJI_AUTO_COMPLETE_CONTAINER); + }; + /** * Get a message element. * @@ -1001,6 +1011,26 @@ function( } }; + /** + * Hide or show the emoji auto complete. + * + * @param {Object} header The header container element. + * @param {Object} body The body container element. + * @param {Object} footer The footer container element. + * @param {Bool} show Should the emoji picker be visible. + */ + var renderShowEmojiAutoComplete = function(header, body, footer, show) { + var container = getEmojiAutoCompleteContainer(footer); + + if (show) { + container.removeClass('hidden'); + container.attr('aria-hidden', false); + } else { + container.addClass('hidden'); + container.attr('aria-hidden', true); + } + }; + /** * Show a confirmation dialogue * @@ -1669,7 +1699,8 @@ function( isMuted: renderIsMuted, loadingConfirmAction: renderLoadingConfirmAction, inEditMode: renderInEditMode, - showEmojiPicker: renderShowEmojiPicker + showEmojiPicker: renderShowEmojiPicker, + showEmojiAutoComplete: renderShowEmojiAutoComplete, }, { // Scrolling should be last to make sure everything diff --git a/message/amd/src/message_drawer_view_conversation_state_manager.js b/message/amd/src/message_drawer_view_conversation_state_manager.js index 7adbf47c216..ac227bd999c 100644 --- a/message/amd/src/message_drawer_view_conversation_state_manager.js +++ b/message/amd/src/message_drawer_view_conversation_state_manager.js @@ -145,6 +145,7 @@ define(['jquery'], function($) { pendingSendMessageIds: [], pendingDeleteConversation: false, selectedMessageIds: [], + showEmojiAutoComplete: false, showEmojiPicker: false }; }; @@ -541,6 +542,19 @@ define(['jquery'], function($) { return newState; }; + /** + * Set whether emojis auto complete suggestions should be shown. + * + * @param {Object} state Current state. + * @param {Bool} show Show the autocomplete + * @return {Object} New state with array of pending delete message ids. + */ + var setShowEmojiAutoComplete = function(state, show) { + var newState = cloneState(state); + newState.showEmojiAutoComplete = show; + return newState; + }; + /** * Set the state pending block userids. * @@ -845,6 +859,7 @@ define(['jquery'], function($) { setMessagesSendPendingById: setMessagesSendPendingById, setMessagesSendSuccessById: setMessagesSendSuccessById, setMessagesSendFailById: setMessagesSendFailById, + setShowEmojiAutoComplete: setShowEmojiAutoComplete, setShowEmojiPicker: setShowEmojiPicker, addPendingBlockUsersById: addPendingBlockUsersById, addPendingRemoveContactsById: addPendingRemoveContactsById, diff --git a/message/templates/message_drawer_view_conversation_footer_content.mustache b/message/templates/message_drawer_view_conversation_footer_content.mustache index 0e49ea29714..683d65a91e5 100644 --- a/message/templates/message_drawer_view_conversation_footer_content.mustache +++ b/message/templates/message_drawer_view_conversation_footer_content.mustache @@ -35,6 +35,13 @@ }} +