diff --git a/lib/behat/classes/partial_named_selector.php b/lib/behat/classes/partial_named_selector.php index 6d73f4a9077..baa9d4f1a1b 100644 --- a/lib/behat/classes/partial_named_selector.php +++ b/lib/behat/classes/partial_named_selector.php @@ -93,6 +93,7 @@ class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSele 'group_message_header' => 'group_message_header', 'group_message_member' => 'group_message_member', 'group_message_tab' => 'group_message_tab', + 'group_message_message_content' => 'group_message_message_content', 'icon' => 'icon', 'link' => 'link', 'link_or_button' => 'link_or_button', @@ -169,6 +170,9 @@ XPATH XPATH , 'group_message_tab' => << << << array('user', 'contact'), 'switchids' => array('user' => 'userid', 'contact' => 'contactid') ), + 'group messages' => array( + 'datagenerator' => 'group_messages', + 'required' => array('user', 'group', 'message'), + 'switchids' => array('user' => 'userid', 'group' => 'groupid') + ), ); /** @@ -887,6 +892,10 @@ class behat_data_generators extends behat_base { * @return void */ protected function process_private_messages(array $data) { + if (empty($data['format'])) { + $data['format'] = 'FORMAT_PLAIN'; + } + if (!$conversationid = \core_message\api::get_conversation_between_users([$data['userid'], $data['contactid']])) { $conversation = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, @@ -894,7 +903,48 @@ class behat_data_generators extends behat_base { ); $conversationid = $conversation->id; } - \core_message\api::send_message_to_conversation($data['userid'], $conversationid, $data['message'], FORMAT_PLAIN); + \core_message\api::send_message_to_conversation( + $data['userid'], + $conversationid, + $data['message'], + constant($data['format']) + ); + } + + /** + * Send a new message from user to a group conversation + * + * @param array $data + * @return void + */ + protected function process_group_messages(array $data) { + global $DB; + + if (empty($data['format'])) { + $data['format'] = 'FORMAT_PLAIN'; + } + + $group = $DB->get_record('groups', ['id' => $data['groupid']]); + $coursecontext = context_course::instance($group->courseid); + if (!$conversation = \core_message\api::get_conversation_by_area('core_group', 'groups', $data['groupid'], + $coursecontext->id)) { + $members = $DB->get_records_menu('groups_members', ['groupid' => $data['groupid']], '', 'userid, id'); + $conversation = \core_message\api::create_conversation( + \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, + array_keys($members), + $group->name, + \core_message\api::MESSAGE_CONVERSATION_ENABLED, + 'core_group', + 'groups', + $group->id, + $coursecontext->id); + } + \core_message\api::send_message_to_conversation( + $data['userid'], + $conversation->id, + $data['message'], + constant($data['format']) + ); } /** diff --git a/message/amd/build/message_drawer_view_conversation.min.js b/message/amd/build/message_drawer_view_conversation.min.js index 3412c9efa2f..239d1533d2f 100644 --- a/message/amd/build/message_drawer_view_conversation.min.js +++ b/message/amd/build/message_drawer_view_conversation.min.js @@ -1 +1 @@ -define(["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"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var p={},q=null,r=!1,s=0,t=null,u=!0,v=!1,w=null,x=j.NEWEST_MESSAGES_FIRST,y=j.LOAD_MESSAGE_LIMIT,z=j.INITIAL_NEW_MESSAGE_POLL_TIMEOUT,A=j.SELECTORS,B=j.CONVERSATION_TYPES,C=function(){if(!q||q.type!=B.PRIVATE)return null;var a=q.loggedInUserId,b=Object.keys(q.members).filter(function(b){return a!=b});return b.length?b[0]:null},D=function(a){return Object.keys(p).reduce(function(b,c){if(!b){var d=p[c].state;d.type==B.PRIVATE&&a in d.members&&(b=d.id)}return b},null)},E=function(a){return{id:parseInt(a.attr("data-user-id"),10),fullname:null,profileimageurl:null,profileimageurlsmall:null,isonline:null,showonlinestatus:null,isblocked:null,iscontact:null,isdeleted:null,canmessage:null,requirescontact:null,contactrequests:[]}},F=function(){return s},G=function(a){s=a,p[q.id].messagesOffset=a},H=function(){return r},I=function(a){r=a,p[q.id].loadedAllMessages=a},J=function(a){return a.find(A.MESSAGES_CONTAINER)},K=function(b){return{id:b.id,name:b.name,subname:b.subname,imageUrl:b.imageUrl,isFavourite:b.isFavourite,type:b.type,totalMemberCount:b.totalMemberCount,loggedInUserId:b.loggedInUserId,messages:b.messages.map(function(b){return a.extend({},b)}),members:Object.keys(b.members).map(function(c){var d=a.extend({},b.members[c]);return d.contactrequests=b.members[c].contactrequests.map(function(b){return a.extend({},b)}),d})}},L=function(a,b){var c=a.id,d=m.setLoadingMembers(q,!0);return d=m.setLoadingMessages(d,!0),w(d).then(function(){return h.getMemberInfo(c,[b],!0,!0)}).then(function(a){if(a.length)return a[0];throw new Error("Unable to load other user profile")}).then(function(b){var c=m.addMembers(q,[b,a]);return c=m.setLoadingMembers(c,!1),c=m.setLoadingMessages(c,!1),c=m.setName(c,b.fullname),c=m.setType(c,1),c=m.setImageUrl(c,b.profileimageurl),c=m.setTotalMemberCount(c,2),w(c).then(function(){return b})})["catch"](function(a){var b=m.setLoadingMembers(q,!1);w(b),e.exception(a)})},M=function(a,b){var c=a.members.filter(function(a){return a.id!=b}),d=c.length?c[0]:null,e=a.name,f=a.imageurl;a.type==B.PRIVATE&&(e=e||d?d.fullname:"",f=f||d?d.profileimageurl:"");var g=m.addMembers(q,a.members);return g=m.setName(g,e),g=m.setSubname(g,a.subname),g=m.setType(g,a.type),g=m.setImageUrl(g,f),g=m.setTotalMemberCount(g,a.membercount),g=m.setIsFavourite(g,a.isfavourite),g=m.addMessages(g,a.messages)},N=function(a,b,c,d,f){var g=b.id,i=m.setLoadingMembers(q,!0);return i=m.setLoadingMessages(i,!0),w(i).then(function(){return h.getConversation(g,a,!0,!0,0,0,c+1,d,f)}).then(function(a){return a.messages.length>c?a.messages=a.messages.slice(1):I(!0),G(d+c),a}).then(function(a){var c=a.members.filter(function(a){return a.id==b.id});c.length<1&&(a.members=a.members.concat([b]));var d=M(a,b.id);return d=m.setLoadingMembers(d,!1),d=m.setLoadingMessages(d,!1),w(d).then(function(){return a})}).then(function(){return R(a)})["catch"](function(a){var b=m.setLoadingMembers(q,!1);b=m.setLoadingMessages(b,!1),w(b),e.exception(a)})},O=function(a,b,c,d){var f=a.members.filter(function(a){return a.id==b.id});f.length<1&&(a.members=a.members.concat([b]));var g=M(a,b.id);g=m.setLoadingMembers(g,!1),g=m.setLoadingMessages(g,!0);var h=a.messages.length;return w(g).then(function(){if(hb?a.messages=a.messages.slice(0,-1):I(!0),a):a}).then(function(a){var b=a.members.filter(function(a){return!(a.id in q.members)}),c=m.addMembers(q,b);return c=m.addMessages(c,a.messages),c=m.setLoadingMessages(c,!1),w(c).then(function(){return a})})["catch"](function(a){var b=m.setLoadingMessages(q,!1);throw w(b),a})},Q=function(b,c){return function(){var d=q.messages,e=d.length?d[d.length-1]:null;if(e&&!u&&!v){for(var g=[],h=d.length-1;h>=0;h--){var j=d[h];if(j.timeCreated!==e.timeCreated)break;g.push(j.id)}return P(b,0,0,c,g,e.timeCreated).then(function(a){if(a.messages.length){t.restart();var c=K(q);return f.publish(i.CONVERSATION_NEW_LAST_MESSAGE,c),R(b)}return a})}return a.Deferred().resolve().promise()}},R=function(a){var b=q.loggedInUserId;return h.markAllConversationMessagesAsRead(b,a).then(function(){var b=m.markMessagesAsRead(q,q.messages);return f.publish(i.CONVERSATION_READ,a),w(b)})},S=function(a){return ea(a).then(function(){var b=m.addPendingBlockUsersById(q,[a]);return w(b)})},T=function(a){var b=m.setLoadingConfirmAction(q,!0);return w(b).then(function(){return h.blockUser(q.loggedInUserId,a)}).then(function(b){var c=m.addMembers(q,[b]);return c=m.removePendingBlockUsersById(c,[a]),c=m.setLoadingConfirmAction(c,!1),f.publish(i.CONTACT_BLOCKED,a),w(c)})},U=function(a){return ea(a).then(function(){var b=m.addPendingUnblockUsersById(q,[a]);return w(b)})},V=function(a){var b=m.setLoadingConfirmAction(q,!0);return w(b).then(function(){return h.unblockUser(q.loggedInUserId,a)}).then(function(b){var c=m.addMembers(q,[b]);return c=m.removePendingUnblockUsersById(c,[a]),c=m.setLoadingConfirmAction(c,!1),f.publish(i.CONTACT_UNBLOCKED,a),w(c)})},W=function(a){return ea(a).then(function(){var b=m.addPendingRemoveContactsById(q,[a]);return w(b)})},X=function(a){var b=m.setLoadingConfirmAction(q,!0);return w(b).then(function(){return h.deleteContacts(q.loggedInUserId,[a])}).then(function(b){var c=m.addMembers(q,b);return c=m.removePendingRemoveContactsById(c,[a]),c=m.setLoadingConfirmAction(c,!1),f.publish(i.CONTACT_REMOVED,a),w(c)})},Y=function(a){return ea(a).then(function(){var b=m.addPendingAddContactsById(q,[a]);return w(b)})},Z=function(a){var b=m.setLoadingConfirmAction(q,!0);return w(b).then(function(){return h.createContactRequest(q.loggedInUserId,a)}).then(function(a){if(!a.request)throw new Error(a.warnings[0].message);return a.request}).then(function(b){var c=m.removePendingAddContactsById(q,[a]);return c=m.addContactRequests(c,[b]),c=m.setLoadingConfirmAction(c,!1),w(c)})},$=function(){var a=q.loggedInUserId,b=q.id;return h.setFavouriteConversations(a,[b]).then(function(){var a=m.setIsFavourite(q,!0);return w(a)}).then(function(){return f.publish(i.CONVERSATION_SET_FAVOURITE,K(q))})},_=function(){var a=q.loggedInUserId,b=q.id;return h.unsetFavouriteConversations(a,[b]).then(function(){var a=m.setIsFavourite(q,!1);return w(a)}).then(function(){return f.publish(i.CONVERSATION_UNSET_FAVOURITE,K(q))})},aa=function(a){var b=q.selectedMessageIds;return ea(a).then(function(){var a=m.addPendingDeleteMessagesById(q,b);return w(a)})},ba=function(){var a=q.pendingDeleteMessageIds,b=m.setLoadingConfirmAction(q,!0);return w(b).then(function(){return h.deleteMessages(q.loggedInUserId,a)}).then(function(){var b=m.removeMessagesById(q,a);b=m.removePendingDeleteMessagesById(b,a),b=m.removeSelectedMessagesById(b,a),b=m.setLoadingConfirmAction(b,!1);var c=q.messages[q.messages.length-1],d=b.messages.length?b.messages[b.messages.length-1]:null;if(d&&d.id!=c.id){var e=K(b);f.publish(i.CONVERSATION_NEW_LAST_MESSAGE,e)}else b.messages.length||f.publish(i.CONVERSATION_DELETED,b.id);return w(b)})},ca=function(a){return ea(a).then(function(){var a=m.setPendingDeleteConversation(q,!0);return w(a)})},da=function(){var a=m.setLoadingConfirmAction(q,!0);return w(a).then(function(){return h.deleteConversation(q.loggedInUserId,q.id)}).then(function(){var a=m.removeMessages(q,q.messages);return a=m.removeSelectedMessagesById(a,q.selectedMessageIds),a=m.setPendingDeleteConversation(a,!1),a=m.setLoadingConfirmAction(a,!1),f.publish(i.CONVERSATION_DELETED,a.id),w(a)})},ea=function(a){var b=q.pendingDeleteMessageIds,c=m.removePendingAddContactsById(q,[a]);return c=m.removePendingRemoveContactsById(c,[a]),c=m.removePendingUnblockUsersById(c,[a]),c=m.removePendingBlockUsersById(c,[a]),c=m.removePendingDeleteMessagesById(c,b),c=m.setPendingDeleteConversation(c,!1),w(c)},fa=function(a){var b=q.loggedInUserId,c=q.members[a].contactrequests.filter(function(a){return a.requesteduserid==b}),d=c[0],e=m.setLoadingConfirmAction(q,!0);return w(e).then(function(){return h.acceptContactRequest(a,b)}).then(function(a){var b=m.removeContactRequests(q,[d]);return b=m.addMembers(q,[a]),b=m.setLoadingConfirmAction(b,!1),w(b)}).then(function(){f.publish(i.CONTACT_ADDED,q.members[a]),f.publish(i.CONTACT_REQUEST_ACCEPTED,d)})},ga=function(a){var b=q.loggedInUserId,c=q.members[a].contactrequests.filter(function(a){return a.requesteduserid==b}),d=c[0],e=m.setLoadingConfirmAction(q,!0);return w(e).then(function(){return h.declineContactRequest(a,b)}).then(function(a){var b=m.removeContactRequests(q,[d]);return b=m.addMembers(q,[a]),b=m.setLoadingConfirmAction(b,!1),w(b)}).then(function(){f.publish(i.CONTACT_REQUEST_DECLINED,d)})},ha=function(a,b){v=!0;var c=m.setSendingMessage(q,!0),d=null;return w(c).then(function(){if(a||q.type!=B.PRIVATE)return h.sendMessageToConversation(a,b);var c=C();return h.sendMessageToUser(c,b).then(function(a){return d=parseInt(a.conversationid,10),a})}).then(function(a){var b=m.addMessages(q,[a]);b=m.setSendingMessage(b,!1);var c=K(b);return b.id||(b=m.setId(b,d),c.id=d,xa(d),f.publish(i.CONVERSATION_CREATED,c)),w(b).then(function(){v=!1,f.publish(i.CONVERSATION_NEW_LAST_MESSAGE,c)})})["catch"](function(a){v=!1;var b=m.setSendingMessage(q,!1);w(b),e.exception(a)})},ia=function(a){var b=q;return b=q.selectedMessageIds.indexOf(a)>-1?m.removeSelectedMessagesById(q,[a]):m.addSelectedMessagesById(q,[a]),w(b)},ja=function(){return ea(C()).then(function(){var a=m.removeSelectedMessagesById(q,q.selectedMessageIds);return w(a)})},ka=function(a,b,c){return function(d){var e=k.buildPatch(q,d);return l.render(a,b,c,e).then(function(){q=d,d.id&&(p[d.id]={state:d,messagesOffset:F(),loadedAllMessages:H()})})}},la=function(a){return function(b,c){q.loadingConfirmAction||a(C())["catch"](function(a){var b=m.setLoadingConfirmAction(q,!1);w(b),e.exception(a)}),c.originalEvent.preventDefault()}},ma=function(b,c){var d=a(b.target),e=d.closest(A.FOOTER_CONTAINER),f=e.find(A.MESSAGE_TEXT_AREA),g=f.val().trim();""!==g&&ha(q.id,g),c.originalEvent.preventDefault()},na=function(b,c){var d=window.getSelection(),f=a(b.target);if(""==d.toString()&&!f.is("a")){var g=f.closest(A.MESSAGE),h=parseInt(g.attr("data-message-id"),10);ia(h)["catch"](e.exception),c.originalEvent.preventDefault()}},oa=function(a,b){ja()["catch"](e.exception),b.originalEvent.preventDefault()},pa=function(a,b){var c=C(),d=q.members[c];n.go(o.VIEW_CONTACT,d),b.originalEvent.preventDefault()},qa=function(a,b){$()["catch"](e.exception),b.originalEvent.preventDefault()},ra=function(a,b){_()["catch"](e.exception),b.originalEvent.preventDefault()},sa=function(a,b){n.go(o.VIEW_GROUP_INFO,{id:q.id,name:q.name,subname:q.subname,imageUrl:q.imageUrl,totalMemberCount:q.totalMemberCount},q.loggedInUserId),b.originalEvent.preventDefault()},ta=[[A.ACTION_REQUEST_BLOCK,la(S)],[A.ACTION_REQUEST_UNBLOCK,la(U)],[A.ACTION_REQUEST_ADD_CONTACT,la(Y)],[A.ACTION_REQUEST_REMOVE_CONTACT,la(W)],[A.ACTION_REQUEST_DELETE_CONVERSATION,la(ca)],[A.ACTION_CANCEL_EDIT_MODE,oa],[A.ACTION_VIEW_CONTACT,pa],[A.ACTION_VIEW_GROUP_INFO,sa],[A.ACTION_CONFIRM_FAVOURITE,qa],[A.ACTION_CONFIRM_UNFAVOURITE,ra]],ua=[[A.ACTION_CANCEL_CONFIRM,la(ea)],[A.ACTION_CONFIRM_BLOCK,la(T)],[A.ACTION_CONFIRM_UNBLOCK,la(V)],[A.ACTION_CONFIRM_ADD_CONTACT,la(Z)],[A.ACTION_CONFIRM_REMOVE_CONTACT,la(X)],[A.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES,la(ba)],[A.ACTION_CONFIRM_DELETE_CONVERSATION,la(da)],[A.ACTION_REQUEST_ADD_CONTACT,la(Y)],[A.ACTION_ACCEPT_CONTACT_REQUEST,la(fa)],[A.ACTION_DECLINE_CONTACT_REQUEST,la(ga)],[A.MESSAGE,na]],va=[[A.SEND_MESSAGE_BUTTON,ma],[A.ACTION_REQUEST_DELETE_SELECTED_MESSAGES,la(aa)],[A.ACTION_REQUEST_ADD_CONTACT,la(Y)],[A.ACTION_REQUEST_UNBLOCK,la(U)]],wa=function(a,c,g){var h=!1,j=J(c);b.init(g),d.define(a,[d.events.activate]),d.define(c,[d.events.activate]),d.define(g,[d.events.activate,d.events.enter]),d.define(j,[d.events.scrollTop,d.events.scrollLock]),j.on(d.events.scrollTop,function(a,b){var c=Object.keys(q.members).length>1;if(!u&&!h&&!H()&&c){h=!0;var d=m.setLoadingMessages(q,!0);w(d).then(function(){return P(q.id,y,F(),x,[])}).then(function(){h=!1,G(F()+y)})["catch"](function(a){h=!1,e.exception(a)})}b.originalEvent.preventDefault()}),ta.forEach(function(b){var c=b[0],e=b[1];a.on(d.events.activate,c,e)}),ua.forEach(function(a){var b=a[0],e=a[1];c.on(d.events.activate,b,e)}),va.forEach(function(a){var b=a[0],c=a[1];g.on(d.events.activate,b,c)}),g.on(d.events.enter,A.MESSAGE_TEXT_AREA,function(a,b){var c=g.attr("data-enter-to-send");c&&"false"!=c&&"0"!=c&&ma(a,b)}),f.subscribe(i.ROUTE_CHANGED,function(a){t&&a.route!=o.VIEW_CONVERSATION&&t.stop()})},xa=function(a){t&&t.stop(),t=new c(Q(a,x),function(a){return a?2*a:z}),t.start()},ya=function(a,b,c){var d=c.id,e=parseInt(a.attr("data-midnight"),10),f=m.buildInitialState(e,d,b);return q||(q=f),t&&t.stop(),w(f)},za=function(a,b,c){return ya(a,null,b).then(function(){return h.getConversationBetweenUsers(b.id,c,!0,!0,0,0,y,0,x).then(function(c){return Ba(a,c,b)})["catch"](function(){return L(b,c)})})},Aa=function(a,b,c){var d=null;return b in p&&(d=p[b]),ya(a,b,c).then(function(){if(d){var a=d.state;return a=m.setLoadingMessages(a,!1),a=m.setLoadingMembers(a,!1),G(d.messagesOffset),I(d.loadedAllMessages),w(a)}return N(b,c,y,0,x)}).then(function(){return xa(b)})},Ba=function(a,b,c){var d=null;return b.id in p&&(d=p[b.id]),ya(a,b.id,c).then(function(){if(d){var a=d.state;return a=m.setLoadingMessages(a,!1),a=m.setLoadingMembers(a,!1),G(d.messagesOffset),I(d.loadedAllMessages),w(a)}return O(b,c,y,x)}).then(function(){return xa(b.id)})},Ca=function(b,c,d,f,g,h){var i=null,k=null;f&&null!==f&&"object"==typeof f?(i=f,k=parseInt(i.id,10)):(i=null,k=parseInt(f,10),k=isNaN(k)?null:k),!k&&g&&h&&(k=D(h)),c.attr("data-init")||(w=ka(b,c,d),wa(b,c,d),c.attr("data-init",!0));var l=!q||q.id!=k||h&&h!=C();if(l){u=!0;var m=null,n=E(c);return m=i?Ba(c,i,n,h):k?Aa(c,k,n,h):za(c,n,h),m.then(function(){u=!1,b.find(j.SELECTORS.CAN_RECEIVE_FOCUS).first().focus()})["catch"](function(a){u=!1,e.exception(a)})}if(xa(k),q.type==B.PRIVATE&&g){var o=C();switch(g){case"block":return S(o);case"unblock":return U(o);case"add-contact":return Y(o);case"remove-contact":return W(o)}}return a.Deferred().resolve().promise()},Da=function(){return g.get_string("messagedrawerviewconversation","core_message",q.name)};return{show:Ca,description:Da}}); \ No newline at end of file +define(["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"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var p={},q=null,r=!1,s=0,t=null,u=!1,v=[],w=!0,x=!1,y=null,z=j.NEWEST_MESSAGES_FIRST,A=j.LOAD_MESSAGE_LIMIT,B=j.INITIAL_NEW_MESSAGE_POLL_TIMEOUT,C=j.SELECTORS,D=j.CONVERSATION_TYPES,E=function(){if(!q||q.type!=D.PRIVATE)return null;var a=q.loggedInUserId,b=Object.keys(q.members).filter(function(b){return a!=b});return b.length?b[0]:null},F=function(a){return Object.keys(p).reduce(function(b,c){if(!b){var d=p[c].state;d.type==D.PRIVATE&&a in d.members&&(b=d.id)}return b},null)},G=function(a){return{id:parseInt(a.attr("data-user-id"),10),fullname:null,profileimageurl:null,profileimageurlsmall:null,isonline:null,showonlinestatus:null,isblocked:null,iscontact:null,isdeleted:null,canmessage:null,requirescontact:null,contactrequests:[]}},H=function(){return s},I=function(a){s=a,p[q.id].messagesOffset=a},J=function(){return r},K=function(a){r=a,p[q.id].loadedAllMessages=a},L=function(a){return a.find(C.MESSAGES_CONTAINER)},M=function(b){return{id:b.id,name:b.name,subname:b.subname,imageUrl:b.imageUrl,isFavourite:b.isFavourite,type:b.type,totalMemberCount:b.totalMemberCount,loggedInUserId:b.loggedInUserId,messages:b.messages.map(function(b){return a.extend({},b)}),members:Object.keys(b.members).map(function(c){var d=a.extend({},b.members[c]);return d.contactrequests=b.members[c].contactrequests.map(function(b){return a.extend({},b)}),d})}},N=function(a,b){var c=a.id,d=m.setLoadingMembers(q,!0);return d=m.setLoadingMessages(d,!0),y(d),h.getMemberInfo(c,[b],!0,!0).then(function(a){if(a.length)return a[0];throw new Error("Unable to load other user profile")}).then(function(b){var c=m.addMembers(q,[b,a]);return c=m.setLoadingMembers(c,!1),c=m.setLoadingMessages(c,!1),c=m.setName(c,b.fullname),c=m.setType(c,1),c=m.setImageUrl(c,b.profileimageurl),c=m.setTotalMemberCount(c,2),y(c),b})["catch"](function(a){var b=m.setLoadingMembers(q,!1);y(b),e.exception(a)})},O=function(a,b){var c=a.members.filter(function(a){return a.id!=b}),d=c.length?c[0]:null,e=a.name,f=a.imageurl;a.type==D.PRIVATE&&(e=e||d?d.fullname:"",f=f||d?d.profileimageurl:"");var g=m.addMembers(q,a.members);return g=m.setName(g,e),g=m.setSubname(g,a.subname),g=m.setType(g,a.type),g=m.setImageUrl(g,f),g=m.setTotalMemberCount(g,a.membercount),g=m.setIsFavourite(g,a.isfavourite),g=m.addMessages(g,a.messages)},P=function(a,b,c,d,f){var g=b.id,i=m.setLoadingMembers(q,!0);return i=m.setLoadingMessages(i,!0),y(i),h.getConversation(g,a,!0,!0,0,0,c+1,d,f).then(function(a){return a.messages.length>c?a.messages=a.messages.slice(1):K(!0),I(d+c),a}).then(function(a){var c=a.members.filter(function(a){return a.id==b.id});c.length<1&&(a.members=a.members.concat([b]));var d=O(a,b.id);return d=m.setLoadingMembers(d,!1),d=m.setLoadingMessages(d,!1),y(d).then(function(){return a})}).then(function(){return T(a)})["catch"](function(a){var b=m.setLoadingMembers(q,!1);b=m.setLoadingMessages(b,!1),y(b),e.exception(a)})},Q=function(a,b,c,d){var f=a.members.filter(function(a){return a.id==b.id});f.length<1&&(a.members=a.members.concat([b]));var g=a.messages.length,h=g>=c,i=O(a,b.id);i=m.setLoadingMembers(i,!1),i=m.setLoadingMessages(i,!h);var j=y(i);return j.then(function(){return h?{messages:a.messages}:R(a.id,c,g,d,[])}).then(function(){var a=q.messages;return I(a.length),T(q.id),a})["catch"](e.exception)},R=function(a,b,c,d,e,f){return h.getMessages(q.loggedInUserId,a,b?b+1:b,c,d,f).then(function(a){return a.messages.length&&e.length&&(a.messages=a.messages.filter(function(a){return e.indexOf(parseInt(a.id,10))<0})),a}).then(function(a){return b?(a.messages.length>b?a.messages=a.messages.slice(0,-1):K(!0),a):a}).then(function(a){var b=a.members.filter(function(a){return!(a.id in q.members)}),c=m.addMembers(q,b);return c=m.addMessages(c,a.messages),c=m.setLoadingMessages(c,!1),y(c).then(function(){return a})})["catch"](function(a){var b=m.setLoadingMessages(q,!1);throw y(b),a})},S=function(b,c){return function(){var d=q.messages,e=d.length?d[d.length-1]:null;if(e&&!w&&!x){for(var g=[],h=d.length-1;h>=0;h--){var j=d[h];if(j.timeCreated!==e.timeCreated)break;g.push(j.id)}return R(b,0,0,c,g,e.timeCreated).then(function(a){if(a.messages.length){t.restart();var c=M(q);return f.publish(i.CONVERSATION_NEW_LAST_MESSAGE,c),T(b)}return a})}return a.Deferred().resolve().promise()}},T=function(a){var b=q.loggedInUserId;return h.markAllConversationMessagesAsRead(b,a).then(function(){var b=m.markMessagesAsRead(q,q.messages);return f.publish(i.CONVERSATION_READ,a),y(b)})},U=function(a){ga(a);var b=m.addPendingBlockUsersById(q,[a]);y(b)},V=function(a){var b=m.setLoadingConfirmAction(q,!0);return y(b),h.blockUser(q.loggedInUserId,a).then(function(b){var c=m.addMembers(q,[b]);return c=m.removePendingBlockUsersById(c,[a]),c=m.setLoadingConfirmAction(c,!1),f.publish(i.CONTACT_BLOCKED,a),y(c)})},W=function(a){ga(a);var b=m.addPendingUnblockUsersById(q,[a]);y(b)},X=function(a){var b=m.setLoadingConfirmAction(q,!0);return y(b),h.unblockUser(q.loggedInUserId,a).then(function(b){var c=m.addMembers(q,[b]);return c=m.removePendingUnblockUsersById(c,[a]),c=m.setLoadingConfirmAction(c,!1),f.publish(i.CONTACT_UNBLOCKED,a),y(c)})},Y=function(a){ga(a);var b=m.addPendingRemoveContactsById(q,[a]);y(b)},Z=function(a){var b=m.setLoadingConfirmAction(q,!0);return y(b),h.deleteContacts(q.loggedInUserId,[a]).then(function(b){var c=m.addMembers(q,b);return c=m.removePendingRemoveContactsById(c,[a]),c=m.setLoadingConfirmAction(c,!1),f.publish(i.CONTACT_REMOVED,a),y(c)})},$=function(a){ga(a);var b=m.addPendingAddContactsById(q,[a]);y(b)},_=function(a){var b=m.setLoadingConfirmAction(q,!0);return y(b),h.createContactRequest(q.loggedInUserId,a).then(function(a){if(!a.request)throw new Error(a.warnings[0].message);return a.request}).then(function(b){var c=m.removePendingAddContactsById(q,[a]);return c=m.addContactRequests(c,[b]),c=m.setLoadingConfirmAction(c,!1),y(c)})},aa=function(){var a=q.loggedInUserId,b=q.id;return h.setFavouriteConversations(a,[b]).then(function(){var a=m.setIsFavourite(q,!0);return y(a)}).then(function(){return f.publish(i.CONVERSATION_SET_FAVOURITE,M(q))})},ba=function(){var a=q.loggedInUserId,b=q.id;return h.unsetFavouriteConversations(a,[b]).then(function(){var a=m.setIsFavourite(q,!1);return y(a)}).then(function(){return f.publish(i.CONVERSATION_UNSET_FAVOURITE,M(q))})},ca=function(a){var b=q.selectedMessageIds;ga(a);var c=m.addPendingDeleteMessagesById(q,b);y(c)},da=function(){var a=q.pendingDeleteMessageIds,b=m.setLoadingConfirmAction(q,!0);return y(b),h.deleteMessages(q.loggedInUserId,a).then(function(){var b=m.removeMessagesById(q,a);b=m.removePendingDeleteMessagesById(b,a),b=m.removeSelectedMessagesById(b,a),b=m.setLoadingConfirmAction(b,!1);var c=q.messages[q.messages.length-1],d=b.messages.length?b.messages[b.messages.length-1]:null;if(d&&d.id!=c.id){var e=M(b);f.publish(i.CONVERSATION_NEW_LAST_MESSAGE,e)}else b.messages.length||f.publish(i.CONVERSATION_DELETED,b.id);return y(b)})},ea=function(a){ga(a);var b=m.setPendingDeleteConversation(q,!0);y(b)},fa=function(){var a=m.setLoadingConfirmAction(q,!0);return y(a),h.deleteConversation(q.loggedInUserId,q.id).then(function(){var a=m.removeMessages(q,q.messages);return a=m.removeSelectedMessagesById(a,q.selectedMessageIds),a=m.setPendingDeleteConversation(a,!1),a=m.setLoadingConfirmAction(a,!1),f.publish(i.CONVERSATION_DELETED,a.id),y(a)})},ga=function(a){var b=q.pendingDeleteMessageIds,c=m.removePendingAddContactsById(q,[a]);c=m.removePendingRemoveContactsById(c,[a]),c=m.removePendingUnblockUsersById(c,[a]),c=m.removePendingBlockUsersById(c,[a]),c=m.removePendingDeleteMessagesById(c,b),c=m.setPendingDeleteConversation(c,!1),y(c)},ha=function(a){var b=q.loggedInUserId,c=q.members[a].contactrequests.filter(function(a){return a.requesteduserid==b}),d=c[0],e=m.setLoadingConfirmAction(q,!0);return y(e),h.acceptContactRequest(a,b).then(function(a){var b=m.removeContactRequests(q,[d]);return b=m.addMembers(q,[a]),b=m.setLoadingConfirmAction(b,!1),y(b)}).then(function(){f.publish(i.CONTACT_ADDED,q.members[a]),f.publish(i.CONTACT_REQUEST_ACCEPTED,d)})},ia=function(a){var b=q.loggedInUserId,c=q.members[a].contactrequests.filter(function(a){return a.requesteduserid==b}),d=c[0],e=m.setLoadingConfirmAction(q,!0);return y(e),h.declineContactRequest(a,b).then(function(a){var b=m.removeContactRequests(q,[d]);return b=m.addMembers(q,[a]),b=m.setLoadingConfirmAction(b,!1),y(b)}).then(function(){f.publish(i.CONTACT_REQUEST_DECLINED,d)})},ja=function(a,b){x=!0;var c=m.setSendingMessage(q,!0),d=null;y(c);var g=null;if(a||q.type!=D.PRIVATE)g=h.sendMessageToConversation(a,b);else{var j=E();g=h.sendMessageToUser(j,b).then(function(a){return d=parseInt(a.conversationid,10),a})}g.then(function(a){var b=m.addMessages(q,[a]);b=m.setSendingMessage(b,!1);var c=M(b);b.id||(b=m.setId(b,d),c.id=d,Aa(d),f.publish(i.CONVERSATION_CREATED,c)),y(b),x=!1,f.publish(i.CONVERSATION_NEW_LAST_MESSAGE,c)})["catch"](function(a){x=!1;var b=m.setSendingMessage(q,!1);y(b),e.exception(a)})},ka=function(a){var b=q;b=q.selectedMessageIds.indexOf(a)>-1?m.removeSelectedMessagesById(q,[a]):m.addSelectedMessagesById(q,[a]),y(b)},la=function(){ga(E());var a=m.removeSelectedMessagesById(q,q.selectedMessageIds);y(a)},ma=function(a,b,c){if(!u&&v.length){u=!0;var d=v.shift();l.render(a,b,c,d.patch).then(function(){u=!1,d.deferred.resolve(!0),ma(a,b,c)})["catch"](function(a){u=!1,d.deferred.reject(a),e.exception(a)})}},na=function(b,c,d){return function(e){var f=k.buildPatch(q,e),g=a.Deferred();return Object.keys(f).length?v.push({patch:f,deferred:g}):g.resolve(!0),q=e,e.id&&(p[e.id]={state:e,messagesOffset:H(),loadedAllMessages:J()}),ma(b,c,d),g.promise()}},oa=function(a){return function(b,c){if(!q.loadingConfirmAction){a(E());var d=m.setLoadingConfirmAction(q,!1);y(d)}c.originalEvent.preventDefault()}},pa=function(b,c){var d=a(b.target),e=d.closest(C.FOOTER_CONTAINER),f=e.find(C.MESSAGE_TEXT_AREA),g=f.val().trim();""!==g&&ja(q.id,g),c.originalEvent.preventDefault()},qa=function(b,c){var d=window.getSelection(),e=a(b.target);if(""==d.toString()&&!e.is("a")){var f=e.closest(C.MESSAGE),g=parseInt(f.attr("data-message-id"),10);ka(g),c.originalEvent.preventDefault()}},ra=function(a,b){la(),b.originalEvent.preventDefault()},sa=function(a,b){var c=E(),d=q.members[c];n.go(o.VIEW_CONTACT,d),b.originalEvent.preventDefault()},ta=function(a,b){aa()["catch"](e.exception),b.originalEvent.preventDefault()},ua=function(a,b){ba()["catch"](e.exception),b.originalEvent.preventDefault()},va=function(a,b){n.go(o.VIEW_GROUP_INFO,{id:q.id,name:q.name,subname:q.subname,imageUrl:q.imageUrl,totalMemberCount:q.totalMemberCount},q.loggedInUserId),b.originalEvent.preventDefault()},wa=[[C.ACTION_REQUEST_BLOCK,oa(U)],[C.ACTION_REQUEST_UNBLOCK,oa(W)],[C.ACTION_REQUEST_ADD_CONTACT,oa($)],[C.ACTION_REQUEST_REMOVE_CONTACT,oa(Y)],[C.ACTION_REQUEST_DELETE_CONVERSATION,oa(ea)],[C.ACTION_CANCEL_EDIT_MODE,ra],[C.ACTION_VIEW_CONTACT,sa],[C.ACTION_VIEW_GROUP_INFO,va],[C.ACTION_CONFIRM_FAVOURITE,ta],[C.ACTION_CONFIRM_UNFAVOURITE,ua]],xa=[[C.ACTION_CANCEL_CONFIRM,oa(ga)],[C.ACTION_CONFIRM_BLOCK,oa(V)],[C.ACTION_CONFIRM_UNBLOCK,oa(X)],[C.ACTION_CONFIRM_ADD_CONTACT,oa(_)],[C.ACTION_CONFIRM_REMOVE_CONTACT,oa(Z)],[C.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES,oa(da)],[C.ACTION_CONFIRM_DELETE_CONVERSATION,oa(fa)],[C.ACTION_REQUEST_ADD_CONTACT,oa($)],[C.ACTION_ACCEPT_CONTACT_REQUEST,oa(ha)],[C.ACTION_DECLINE_CONTACT_REQUEST,oa(ia)],[C.MESSAGE,qa]],ya=[[C.SEND_MESSAGE_BUTTON,pa],[C.ACTION_REQUEST_DELETE_SELECTED_MESSAGES,oa(ca)],[C.ACTION_REQUEST_ADD_CONTACT,oa($)],[C.ACTION_REQUEST_UNBLOCK,oa(W)]],za=function(a,c,g){var h=!1,j=L(c);b.init(g),d.define(a,[d.events.activate]),d.define(c,[d.events.activate]),d.define(g,[d.events.activate,d.events.enter]),d.define(j,[d.events.scrollTop,d.events.scrollLock]),j.on(d.events.scrollTop,function(a,b){var c=Object.keys(q.members).length>1;if(!w&&!h&&!J()&&c){h=!0;var d=m.setLoadingMessages(q,!0);y(d),R(q.id,A,H(),z,[]).then(function(){h=!1,I(H()+A)})["catch"](function(a){h=!1,e.exception(a)})}b.originalEvent.preventDefault()}),wa.forEach(function(b){var c=b[0],e=b[1];a.on(d.events.activate,c,e)}),xa.forEach(function(a){var b=a[0],e=a[1];c.on(d.events.activate,b,e)}),ya.forEach(function(a){var b=a[0],c=a[1];g.on(d.events.activate,b,c)}),g.on(d.events.enter,C.MESSAGE_TEXT_AREA,function(a,b){var c=g.attr("data-enter-to-send");c&&"false"!=c&&"0"!=c&&pa(a,b)}),f.subscribe(i.ROUTE_CHANGED,function(a){t&&a.route!=o.VIEW_CONVERSATION&&t.stop()})},Aa=function(a){t&&t.stop(),t=new c(S(a,z),function(a){return a?2*a:B}),t.start()},Ba=function(a,b,c){w=!0,u=!1,v=[],x=!1;var d=c.id,e=parseInt(a.attr("data-midnight"),10),f=m.buildInitialState(e,d,b);q||(q=f),t&&t.stop(),y(f)},Ca=function(a,b,c){return Ba(a,null,b),h.getConversationBetweenUsers(b.id,c,!0,!0,0,0,A,0,z).then(function(c){return Ea(a,c,b)})["catch"](function(){return N(b,c)})},Da=function(b,c,d){var e=null;c in p&&(e=p[c]),Ba(b,c,d);var f=a.Deferred().resolve({}).promise();if(e){var g=e.state;g=m.setLoadingMessages(g,!1),g=m.setLoadingMembers(g,!1),I(e.messagesOffset),K(e.loadedAllMessages),y(g)}else f=P(c,d,A,0,z);return f.then(function(){return Aa(c)})},Ea=function(b,c,d){var e=null;c.id in p&&(e=p[c.id]),Ba(b,c.id,d);var f=a.Deferred().resolve({}).promise();if(e){var g=e.state;g=m.setLoadingMessages(g,!1),g=m.setLoadingMembers(g,!1),I(e.messagesOffset),K(e.loadedAllMessages),y(g)}else f=Q(c,d,A,z);return f.then(function(){return Aa(c.id)})},Fa=function(b,c,d,f,g,h){var i=null,k=null;f&&null!==f&&"object"==typeof f?(i=f,k=parseInt(i.id,10)):(i=null,k=parseInt(f,10),k=isNaN(k)?null:k),!k&&g&&h&&(k=F(h)),c.attr("data-init")||(y=na(b,c,d),za(b,c,d),c.attr("data-init",!0));var l=!q||q.id!=k||h&&h!=E();if(l){var m=null,n=G(c);return m=i?Ea(c,i,n,h):k?Da(c,k,n,h):Ca(c,n,h),m.then(function(){w=!1,b.find(j.SELECTORS.CAN_RECEIVE_FOCUS).first().focus()})["catch"](function(a){w=!1,e.exception(a)})}if(Aa(k),q.type==D.PRIVATE&&g){var o=E();switch(g){case"block":return U(o);case"unblock":return W(o);case"add-contact":return $(o);case"remove-contact":return Y(o)}}return a.Deferred().resolve().promise()},Ga=function(){return g.get_string("messagedrawerviewconversation","core_message",q.name)};return{show:Fa,description:Ga}}); \ 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 1eafb1473a7..298031cb534 100644 --- a/message/amd/src/message_drawer_view_conversation.js +++ b/message/amd/src/message_drawer_view_conversation.js @@ -97,11 +97,13 @@ function( var loadedAllMessages = false; var messagesOffset = 0; var newMessagesPollTimer = null; + var isRendering = false; + var renderBuffer = []; // If the UI is currently resetting. var isResetting = true; // If the UI is currently sending a message. var isSendingMessage = false; - // This is the render function which will be generated when this module is + // These functions which will be generated when this module is // first called. See generateRenderFunction for details. var render = null; @@ -267,10 +269,9 @@ function( var loggedInUserId = loggedInUserProfile.id; var newState = StateManager.setLoadingMembers(viewState, true); newState = StateManager.setLoadingMessages(newState, true); - return render(newState) - .then(function() { - return Repository.getMemberInfo(loggedInUserId, [otherUserId], true, true); - }) + render(newState); + + return Repository.getMemberInfo(loggedInUserId, [otherUserId], true, true) .then(function(profiles) { if (profiles.length) { return profiles[0]; @@ -286,10 +287,8 @@ function( newState = StateManager.setType(newState, 1); newState = StateManager.setImageUrl(newState, profile.profileimageurl); newState = StateManager.setTotalMemberCount(newState, 2); - return render(newState) - .then(function() { - return profile; - }); + render(newState); + return profile; }) .catch(function(error) { var newState = StateManager.setLoadingMembers(viewState, false); @@ -349,20 +348,19 @@ function( var loggedInUserId = loggedInUserProfile.id; var newState = StateManager.setLoadingMembers(viewState, true); newState = StateManager.setLoadingMessages(newState, true); - return render(newState) - .then(function() { - return Repository.getConversation( - loggedInUserId, - conversationId, - true, - true, - 0, - 0, - messageLimit + 1, - messageOffset, - newestFirst - ); - }) + render(newState); + + return Repository.getConversation( + loggedInUserId, + conversationId, + true, + true, + 0, + 0, + messageLimit + 1, + messageOffset, + newestFirst + ) .then(function(conversation) { if (conversation.messages.length > messageLimit) { conversation.messages = conversation.messages.slice(1); @@ -425,37 +423,30 @@ function( conversation.members = conversation.members.concat([loggedInUserProfile]); } + var messageCount = conversation.messages.length; + var hasLoadedEnoughMessages = messageCount >= messageLimit; var newState = updateStateFromConversation(conversation, loggedInUserProfile.id); newState = StateManager.setLoadingMembers(newState, false); - newState = StateManager.setLoadingMessages(newState, true); - var messageCount = conversation.messages.length; - return render(newState) - .then(function() { - if (messageCount < messageLimit) { + newState = StateManager.setLoadingMessages(newState, !hasLoadedEnoughMessages); + var renderPromise = render(newState); + + return renderPromise.then(function() { + if (!hasLoadedEnoughMessages) { // We haven't got enough messages so let's load some more. - return loadMessages(conversation.id, messageLimit, messageCount, newestFirst, []) - .then(function(result) { - // Give the list of messages to the next handler. - return result.messages; - }); + return loadMessages(conversation.id, messageLimit, messageCount, newestFirst, []); } else { // We've got enough messages. No need to load any more for now. - var newState = StateManager.setLoadingMessages(viewState, false); - return render(newState) - .then(function() { - // Give the list of messages to the next handler. - return conversation.messages; - }); + return {messages: conversation.messages}; } }) - .then(function(messages) { + .then(function() { + var messages = viewState.messages; // Update the offset to reflect the number of messages we've loaded. setMessagesOffset(messages.length); + markConversationAsRead(viewState.id); + return messages; }) - .then(function() { - return markConversationAsRead(conversation.id); - }) .catch(Notification.exception); }; @@ -602,14 +593,12 @@ function( * Tell the statemanager there is request to block a user and run the renderer * to show the block user dialogue. * - * @param {Number} userId User id. - * @return {Promise} Renderer promise. + * @param {Number} userId User id. */ var requestBlockUser = function(userId) { - return cancelRequest(userId).then(function() { - var newState = StateManager.addPendingBlockUsersById(viewState, [userId]); - return render(newState); - }); + cancelRequest(userId); + var newState = StateManager.addPendingBlockUsersById(viewState, [userId]); + render(newState); }; /** @@ -621,10 +610,9 @@ function( */ var blockUser = function(userId) { var newState = StateManager.setLoadingConfirmAction(viewState, true); - return render(newState) - .then(function() { - return Repository.blockUser(viewState.loggedInUserId, userId); - }) + render(newState); + + return Repository.blockUser(viewState.loggedInUserId, userId) .then(function(profile) { var newState = StateManager.addMembers(viewState, [profile]); newState = StateManager.removePendingBlockUsersById(newState, [userId]); @@ -638,14 +626,12 @@ function( * Tell the statemanager there is a request to unblock a user and run the renderer * to show the unblock user dialogue. * - * @param {Number} userId User id of user to unblock. - * @return {Promise} Renderer promise. + * @param {Number} userId User id of user to unblock. */ var requestUnblockUser = function(userId) { - return cancelRequest(userId).then(function() { - var newState = StateManager.addPendingUnblockUsersById(viewState, [userId]); - return render(newState); - }); + cancelRequest(userId); + var newState = StateManager.addPendingUnblockUsersById(viewState, [userId]); + render(newState); }; /** @@ -657,10 +643,9 @@ function( */ var unblockUser = function(userId) { var newState = StateManager.setLoadingConfirmAction(viewState, true); - return render(newState) - .then(function() { - return Repository.unblockUser(viewState.loggedInUserId, userId); - }) + render(newState); + + return Repository.unblockUser(viewState.loggedInUserId, userId) .then(function(profile) { var newState = StateManager.addMembers(viewState, [profile]); newState = StateManager.removePendingUnblockUsersById(newState, [userId]); @@ -674,14 +659,12 @@ function( * Tell the statemanager there is a request to remove a user from the contact list * and run the renderer to show the remove user from contacts dialogue. * - * @param {Number} userId User id of user to remove from contacts. - * @return {Promise} Renderer promise. + * @param {Number} userId User id of user to remove from contacts. */ var requestRemoveContact = function(userId) { - return cancelRequest(userId).then(function() { - var newState = StateManager.addPendingRemoveContactsById(viewState, [userId]); - return render(newState); - }); + cancelRequest(userId); + var newState = StateManager.addPendingRemoveContactsById(viewState, [userId]); + render(newState); }; /** @@ -693,10 +676,9 @@ function( */ var removeContact = function(userId) { var newState = StateManager.setLoadingConfirmAction(viewState, true); - return render(newState) - .then(function() { - return Repository.deleteContacts(viewState.loggedInUserId, [userId]); - }) + render(newState); + + return Repository.deleteContacts(viewState.loggedInUserId, [userId]) .then(function(profiles) { var newState = StateManager.addMembers(viewState, profiles); newState = StateManager.removePendingRemoveContactsById(newState, [userId]); @@ -710,14 +692,12 @@ function( * Tell the statemanager there is a request to add a user to the contact list * and run the renderer to show the add user to contacts dialogue. * - * @param {Number} userId User id of user to add to contacts. - * @return {Promise} Renderer promise. + * @param {Number} userId User id of user to add to contacts. */ var requestAddContact = function(userId) { - return cancelRequest(userId).then(function() { - var newState = StateManager.addPendingAddContactsById(viewState, [userId]); - return render(newState); - }); + cancelRequest(userId); + var newState = StateManager.addPendingAddContactsById(viewState, [userId]); + render(newState); }; /** @@ -729,10 +709,9 @@ function( */ var addContact = function(userId) { var newState = StateManager.setLoadingConfirmAction(viewState, true); - return render(newState) - .then(function() { - return Repository.createContactRequest(viewState.loggedInUserId, userId); - }) + render(newState); + + return Repository.createContactRequest(viewState.loggedInUserId, userId) .then(function(response) { if (!response.request) { throw new Error(response.warnings[0].message); @@ -796,15 +775,13 @@ function( * Tell the statemanager there is a request to delete the selected messages * and run the renderer to show confirm delete messages dialogue. * - * @param {Number} userId User id. - * @return {Promise} Renderer promise. + * @param {Number} userId User id. */ var requestDeleteSelectedMessages = function(userId) { var selectedMessageIds = viewState.selectedMessageIds; - return cancelRequest(userId).then(function() { - var newState = StateManager.addPendingDeleteMessagesById(viewState, selectedMessageIds); - return render(newState); - }); + cancelRequest(userId); + var newState = StateManager.addPendingDeleteMessagesById(viewState, selectedMessageIds); + render(newState); }; /** @@ -816,10 +793,9 @@ function( var deleteSelectedMessages = function() { var messageIds = viewState.pendingDeleteMessageIds; var newState = StateManager.setLoadingConfirmAction(viewState, true); - return render(newState) - .then(function() { - return Repository.deleteMessages(viewState.loggedInUserId, messageIds); - }) + render(newState); + + return Repository.deleteMessages(viewState.loggedInUserId, messageIds) .then(function() { var newState = StateManager.removeMessagesById(viewState, messageIds); newState = StateManager.removePendingDeleteMessagesById(newState, messageIds); @@ -844,14 +820,12 @@ function( * Tell the statemanager there is a request to delete a conversation * and run the renderer to show confirm delete conversation dialogue. * - * @param {Number} userId User id of other user. - * @return {Promise} Renderer promise. + * @param {Number} userId User id of other user. */ var requestDeleteConversation = function(userId) { - return cancelRequest(userId).then(function() { - var newState = StateManager.setPendingDeleteConversation(viewState, true); - return render(newState); - }); + cancelRequest(userId); + var newState = StateManager.setPendingDeleteConversation(viewState, true); + render(newState); }; /** @@ -862,10 +836,9 @@ function( */ var deleteConversation = function() { var newState = StateManager.setLoadingConfirmAction(viewState, true); - return render(newState) - .then(function() { - return Repository.deleteConversation(viewState.loggedInUserId, viewState.id); - }) + render(newState); + + return Repository.deleteConversation(viewState.loggedInUserId, viewState.id) .then(function() { var newState = StateManager.removeMessages(viewState, viewState.messages); newState = StateManager.removeSelectedMessagesById(newState, viewState.selectedMessageIds); @@ -880,7 +853,6 @@ function( * Tell the statemanager to cancel all pending actions. * * @param {Number} userId User id. - * @return {Promise} Renderer promise. */ var cancelRequest = function(userId) { var pendingDeleteMessageIds = viewState.pendingDeleteMessageIds; @@ -890,7 +862,7 @@ function( newState = StateManager.removePendingBlockUsersById(newState, [userId]); newState = StateManager.removePendingDeleteMessagesById(newState, pendingDeleteMessageIds); newState = StateManager.setPendingDeleteConversation(newState, false); - return render(newState); + render(newState); }; /** @@ -908,10 +880,9 @@ function( }); var request = requests[0]; var newState = StateManager.setLoadingConfirmAction(viewState, true); - return render(newState) - .then(function() { - return Repository.acceptContactRequest(userId, loggedInUserId); - }) + render(newState); + + return Repository.acceptContactRequest(userId, loggedInUserId) .then(function(profile) { var newState = StateManager.removeContactRequests(viewState, [request]); newState = StateManager.addMembers(viewState, [profile]); @@ -940,10 +911,9 @@ function( }); var request = requests[0]; var newState = StateManager.setLoadingConfirmAction(viewState, true); - return render(newState) - .then(function() { - return Repository.declineContactRequest(userId, loggedInUserId); - }) + render(newState); + + return Repository.declineContactRequest(userId, loggedInUserId) .then(function(profile) { var newState = StateManager.removeContactRequests(viewState, [request]); newState = StateManager.addMembers(viewState, [profile]); @@ -968,22 +938,24 @@ function( isSendingMessage = true; var newState = StateManager.setSendingMessage(viewState, true); var newConversationId = null; - return render(newState) - .then(function() { - if (!conversationId && viewState.type == CONVERSATION_TYPES.PRIVATE) { - // If it's a new private conversation then we need to use the old - // web service function to create the conversation. - var otherUserId = getOtherUserId(); - return Repository.sendMessageToUser(otherUserId, text) - .then(function(message) { - newConversationId = parseInt(message.conversationid, 10); - return message; - }); - } else { - return Repository.sendMessageToConversation(conversationId, text); - } - }) - .then(function(message) { + render(newState); + + var sendMessagePromise = null; + + if (!conversationId && viewState.type == CONVERSATION_TYPES.PRIVATE) { + // If it's a new private conversation then we need to use the old + // web service function to create the conversation. + var otherUserId = getOtherUserId(); + sendMessagePromise = Repository.sendMessageToUser(otherUserId, text) + .then(function(message) { + newConversationId = parseInt(message.conversationid, 10); + return message; + }); + } else { + sendMessagePromise = Repository.sendMessageToConversation(conversationId, text); + } + + sendMessagePromise.then(function(message) { var newState = StateManager.addMessages(viewState, [message]); newState = StateManager.setSendingMessage(newState, false); var conversation = formatConversationForEvent(newState); @@ -997,12 +969,10 @@ function( PubSub.publish(MessageDrawerEvents.CONVERSATION_CREATED, conversation); } - return render(newState) - .then(function() { - isSendingMessage = false; - PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation); - return; - }); + render(newState); + isSendingMessage = false; + PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation); + return; }) .catch(function(error) { isSendingMessage = false; @@ -1016,7 +986,6 @@ function( * Toggle the selected messages update the statemanager and render the result. * * @param {Number} messageId The id of the message to be toggled - * @return {Promise} Renderer promise. */ var toggleSelectMessage = function(messageId) { var newState = viewState; @@ -1027,7 +996,7 @@ function( newState = StateManager.addSelectedMessagesById(viewState, [messageId]); } - return render(newState); + render(newState); }; /** @@ -1036,10 +1005,41 @@ function( * @return {Promise} Renderer promise. */ var cancelEditMode = function() { - return cancelRequest(getOtherUserId()) + cancelRequest(getOtherUserId()); + var newState = StateManager.removeSelectedMessagesById(viewState, viewState.selectedMessageIds); + render(newState); + }; + + /** + * Process the patches in the render buffer one at a time in order until the + * buffer is empty. + * + * @param {Object} header The conversation header container element. + * @param {Object} body The conversation body container element. + * @param {Object} footer The conversation footer container element. + */ + var processRenderBuffer = function(header, body, footer) { + if (isRendering) { + return; + } + + if (!renderBuffer.length) { + return; + } + + isRendering = true; + var renderable = renderBuffer.shift(); + Renderer.render(header, body, footer, renderable.patch) .then(function() { - var newState = StateManager.removeSelectedMessagesById(viewState, viewState.selectedMessageIds); - return render(newState); + isRendering = false; + renderable.deferred.resolve(true); + // Keep processing the buffer until it's empty. + processRenderBuffer(header, body, footer); + }) + .catch(function(error) { + isRendering = false; + renderable.deferred.reject(error); + Notification.exception(error); }); }; @@ -1054,22 +1054,39 @@ function( var generateRenderFunction = function(header, body, footer) { return function(newState) { var patch = Patcher.buildPatch(viewState, newState); + var deferred = $.Deferred(); + + // Check if the patch has any data. Ignore empty patches. + if (Object.keys(patch).length) { + // Add the patch to the render buffer which gets processed in order. + renderBuffer.push({ + patch: patch, + deferred: deferred + }); + } else { + deferred.resolve(true); + } // This is a great place to add in some console logging if you need // to debug something. You can log the current state, the next state, // and the generated patch and see exactly what will be updated. - return Renderer.render(header, body, footer, patch) - .then(function() { - viewState = newState; - if (newState.id) { - // Only cache created conversations. - stateCache[newState.id] = { - state: newState, - messagesOffset: getMessagesOffset(), - loadedAllMessages: hasLoadedAllMessages() - }; - } - return; - }); + + // Optimistically update the state. We're going to assume that the rendering + // will always succeed. The rendering is asynchronous (annoyingly) so it's buffered + // but it'll reach eventual consistency with the current state. + viewState = newState; + if (newState.id) { + // Only cache created conversations. + stateCache[newState.id] = { + state: newState, + messagesOffset: getMessagesOffset(), + loadedAllMessages: hasLoadedAllMessages() + }; + } + + // Start processing the buffer. + processRenderBuffer(header, body, footer); + + return deferred.promise(); }; }; @@ -1082,12 +1099,9 @@ function( var generateConfirmActionHandler = function(actionCallback) { return function(e, data) { if (!viewState.loadingConfirmAction) { - actionCallback(getOtherUserId()) - .catch(function(error) { - var newState = StateManager.setLoadingConfirmAction(viewState, false); - render(newState); - Notification.exception(error); - }); + actionCallback(getOtherUserId()); + var newState = StateManager.setLoadingConfirmAction(viewState, false); + render(newState); } data.originalEvent.preventDefault(); }; @@ -1135,7 +1149,7 @@ function( var element = target.closest(SELECTORS.MESSAGE); var messageId = parseInt(element.attr('data-message-id'), 10); - toggleSelectMessage(messageId).catch(Notification.exception); + toggleSelectMessage(messageId); data.originalEvent.preventDefault(); }; @@ -1147,7 +1161,7 @@ function( * @param {Object} data Data for this event. */ var handleCancelEditMode = function(e, data) { - cancelEditMode().catch(Notification.exception); + cancelEditMode(); data.originalEvent.preventDefault(); }; @@ -1273,10 +1287,9 @@ function( if (!isResetting && !isLoadingMoreMessages && !hasLoadedAllMessages() && hasMembers) { isLoadingMoreMessages = true; var newState = StateManager.setLoadingMessages(viewState, true); - render(newState) - .then(function() { - return loadMessages(viewState.id, LOAD_MESSAGE_LIMIT, getMessagesOffset(), NEWEST_FIRST, []); - }) + render(newState); + + loadMessages(viewState.id, LOAD_MESSAGE_LIMIT, getMessagesOffset(), NEWEST_FIRST, []) .then(function() { isLoadingMoreMessages = false; setMessagesOffset(getMessagesOffset() + LOAD_MESSAGE_LIMIT); @@ -1355,9 +1368,15 @@ function( * @param {Object} body Conversation body container element. * @param {Number|null} conversationId The conversation id. * @param {Object} loggedInUserProfile The logged in user's profile. - * @return {Promise} Renderer promise. */ var resetState = function(body, conversationId, loggedInUserProfile) { + // Reset all of the states back to the beginning if we're loading a new + // conversation. + isResetting = true; + isRendering = false; + renderBuffer = []; + isSendingMessage = false; + var loggedInUserId = loggedInUserProfile.id; var midnight = parseInt(body.attr('data-midnight'), 10); var initialState = StateManager.buildInitialState(midnight, loggedInUserId, conversationId); @@ -1370,7 +1389,7 @@ function( newMessagesPollTimer.stop(); } - return render(initialState); + render(initialState); }; /** @@ -1384,27 +1403,25 @@ function( var resetNoConversation = function(body, loggedInUserProfile, otherUserId) { // Always reset the state back to the initial state so that the // state manager and patcher can work correctly. - return resetState(body, null, loggedInUserProfile) - .then(function() { - return Repository.getConversationBetweenUsers( - loggedInUserProfile.id, - otherUserId, - true, - true, - 0, - 0, - LOAD_MESSAGE_LIMIT, - 0, - NEWEST_FIRST - ) - .then(function(conversation) { - // Looks like we have a conversation after all! Let's use that. - return resetByConversation(body, conversation, loggedInUserProfile); - }) - .catch(function() { - // Can't find a conversation. Oh well. Just load up a blank one. - return loadEmptyPrivateConversation(loggedInUserProfile, otherUserId); - }); + resetState(body, null, loggedInUserProfile); + return Repository.getConversationBetweenUsers( + loggedInUserProfile.id, + otherUserId, + true, + true, + 0, + 0, + LOAD_MESSAGE_LIMIT, + 0, + NEWEST_FIRST + ) + .then(function(conversation) { + // Looks like we have a conversation after all! Let's use that. + return resetByConversation(body, conversation, loggedInUserProfile); + }) + .catch(function() { + // Can't find a conversation. Oh well. Just load up a blank one. + return loadEmptyPrivateConversation(loggedInUserProfile, otherUserId); }); }; @@ -1424,31 +1441,32 @@ function( // Always reset the state back to the initial state so that the // state manager and patcher can work correctly. - return resetState(body, conversationId, loggedInUserProfile) - .then(function() { - if (cache) { - // We've seen this conversation before so there is no need to - // send any network requests. - var newState = cache.state; - // Reset some loading states just in case they were left weirdly. - newState = StateManager.setLoadingMessages(newState, false); - newState = StateManager.setLoadingMembers(newState, false); - setMessagesOffset(cache.messagesOffset); - setLoadedAllMessages(cache.loadedAllMessages); - return render(newState); - } else { - return loadNewConversation( - conversationId, - loggedInUserProfile, - LOAD_MESSAGE_LIMIT, - 0, - NEWEST_FIRST - ); - } - }) - .then(function() { - return resetMessagePollTimer(conversationId); - }); + resetState(body, conversationId, loggedInUserProfile); + + var promise = $.Deferred().resolve({}).promise(); + if (cache) { + // We've seen this conversation before so there is no need to + // send any network requests. + var newState = cache.state; + // Reset some loading states just in case they were left weirdly. + newState = StateManager.setLoadingMessages(newState, false); + newState = StateManager.setLoadingMembers(newState, false); + setMessagesOffset(cache.messagesOffset); + setLoadedAllMessages(cache.loadedAllMessages); + render(newState); + } else { + promise = loadNewConversation( + conversationId, + loggedInUserProfile, + LOAD_MESSAGE_LIMIT, + 0, + NEWEST_FIRST + ); + } + + return promise.then(function() { + return resetMessagePollTimer(conversationId); + }); }; /** @@ -1467,30 +1485,31 @@ function( // Always reset the state back to the initial state so that the // state manager and patcher can work correctly. - return resetState(body, conversation.id, loggedInUserProfile) - .then(function() { - if (cache) { - // We've seen this conversation before so there is no need to - // send any network requests. - var newState = cache.state; - // Reset some loading states just in case they were left weirdly. - newState = StateManager.setLoadingMessages(newState, false); - newState = StateManager.setLoadingMembers(newState, false); - setMessagesOffset(cache.messagesOffset); - setLoadedAllMessages(cache.loadedAllMessages); - return render(newState); - } else { - return loadExistingConversation( - conversation, - loggedInUserProfile, - LOAD_MESSAGE_LIMIT, - NEWEST_FIRST - ); - } - }) - .then(function() { - return resetMessagePollTimer(conversation.id); - }); + resetState(body, conversation.id, loggedInUserProfile); + + var promise = $.Deferred().resolve({}).promise(); + if (cache) { + // We've seen this conversation before so there is no need to + // send any network requests. + var newState = cache.state; + // Reset some loading states just in case they were left weirdly. + newState = StateManager.setLoadingMessages(newState, false); + newState = StateManager.setLoadingMembers(newState, false); + setMessagesOffset(cache.messagesOffset); + setLoadedAllMessages(cache.loadedAllMessages); + render(newState); + } else { + promise = loadExistingConversation( + conversation, + loggedInUserProfile, + LOAD_MESSAGE_LIMIT, + NEWEST_FIRST + ); + } + + return promise.then(function() { + return resetMessagePollTimer(conversation.id); + }); }; /** @@ -1532,7 +1551,7 @@ function( } if (!body.attr('data-init')) { - // Generate the render function to bind the header, body, and footer + // Generate these functions to bind the header, body, and footer // elements to it so that we don't need to pass them around this module. render = generateRenderFunction(header, body, footer); registerEventListeners(header, body, footer); @@ -1549,9 +1568,6 @@ function( // conversation with. var isNewConversation = !viewState || (viewState.id != conversationId) || (otherUserId && otherUserId != getOtherUserId()); if (isNewConversation) { - // Reset all of the states back to the beginning if we're loading a new - // conversation. - isResetting = true; var renderPromise = null; var loggedInUserProfile = getLoggedInUserProfile(body); if (conversation) { diff --git a/message/tests/behat/delete_messages.feature b/message/tests/behat/delete_messages.feature new file mode 100644 index 00000000000..3f985ebce04 --- /dev/null +++ b/message/tests/behat/delete_messages.feature @@ -0,0 +1,268 @@ +@core @core_message @javascript +Feature: Delete messages from conversations + In order to manage a course group in a course + As a user + I need to be able to delete messages from conversations + + Background: + Given the following "courses" exist: + | fullname | shortname | category | groupmode | + | Course 1 | C1 | 0 | 1 | + And the following "users" exist: + | username | firstname | lastname | email | + | student1 | Student | 1 | student1@example.com | + | student2 | Student | 2 | student2@example.com | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + | student2 | C1 | student | + And the following "groups" exist: + | name | course | idnumber | enablemessaging | + | Group 1 | C1 | G1 | 1 | + And the following "group members" exist: + | user | group | + | student1 | G1 | + | student2 | G1 | + And the following "group messages" exist: + | user | group | message | + | student1 | G1 | Hi! | + | student2 | G1 | How are you? | + | student1 | G1 | Can somebody help me? | + And the following "private messages" exist: + | user | contact | message | + | student1 | student2 | Hi! | + | student2 | student1 | Hello! | + | student1 | student2 | Are you free? | + And the following config values are set as admin: + | messaging | 1 | + + Scenario: Delete a message sent by the user from a group conversation + Given I log in as "student1" + And I open messaging + And "Group 1" "group_message" should exist + And I select "Group 1" conversation in messaging + And I click on "Hi!" "group_message_message_content" + And I click on "How are you?" "group_message_message_content" + And I click on "Can somebody help me?" "group_message_message_content" + And I should see "3" in the "[data-region='message-selected-court']" "css_element" +# Clicking to unselect + And I click on "How are you?" "group_message_message_content" + And I click on "Can somebody help me?" "group_message_message_content" + And I should see "1" in the "[data-region='message-selected-court']" "css_element" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" +# Deleting, so messages should not be there + And I should see "Delete" + And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element" + Then I should not see "Delete" + And I should not see "Hi!" + And I should see "##today##j F##" in the "Group 1" "group_message_conversation" + And I should see "How are you?" in the "Group 1" "group_message_conversation" + And I should see "Can somebody help me?" in the "Group 1" "group_message_conversation" + And I should not see "Messages selected" + + Scenario: Delete two messages from a group conversation; one sent by another user. + Given I log in as "student1" + And I open messaging + And "Group 1" "group_message" should exist + And I select "Group 1" conversation in messaging + And I click on "Hi!" "group_message_message_content" + And I should see "1" in the "[data-region='message-selected-court']" "css_element" + And I click on "How are you?" "group_message_message_content" + And I should see "2" in the "[data-region='message-selected-court']" "css_element" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" +# Deleting, so messages should not be there + And I should see "Delete" + And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element" + Then I should not see "Delete" + And I should not see "Hi!" + And I should see "##today##j F##" in the "Group 1" "group_message_conversation" + And I should not see "How are you?" in the "Group 1" "group_message_conversation" + And I should see "Can somebody help me?" in the "Group 1" "group_message_conversation" + And I should not see "Messages selected" +# Check messages were not deleted for other users + And I log out + And I log in as "student2" + And I open messaging + And I select "Group 1" conversation in messaging + And I should see "Hi!" + And I should see "How are you?" + And I should see "Can somebody help me?" + + Scenario: Cancel deleting two messages from a group conversation + Given I log in as "student1" + And I open messaging + And "Group 1" "group_message" should exist + And I select "Group 1" conversation in messaging + And I click on "Hi!" "group_message_message_content" + And I click on "How are you?" "group_message_message_content" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" +# Canceling deletion, so messages should be there + And I should see "Cancel" + And I click on "//button[@data-action='cancel-confirm']" "xpath_element" + Then I should not see "Cancel" + And I should see "Hi!" + And I should see "How are you?" in the "Group 1" "group_message_conversation" + And I should see "2" in the "[data-region='message-selected-court']" "css_element" + + Scenario: Delete a message sent by the user from a private conversation + Given I log in as "student1" + And I open messaging + And I should see "Private" + And I open the "Private" conversations list + And I should see "Student 2" + And I select "Student 2" conversation in messaging + And I click on "Hi!" "group_message_message_content" + And I should see "1" in the "[data-region='message-selected-court']" "css_element" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" +# Deleting, so messages should not be there + And I should see "Delete" + And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element" + Then I should not see "Delete" + And I should not see "Hi!" + And I should see "##today##j F##" in the "Student 2" "group_message_conversation" + And I should see "Hello!" in the "Student 2" "group_message_conversation" + And I should see "Are you free?" in the "Student 2" "group_message_conversation" + And I should not see "Messages selected" + + Scenario: Delete two messages from a private conversation; one sent by another user + Given I log in as "student1" + And I open messaging + And I should see "Private" + And I open the "Private" conversations list + And I should see "Student 2" + And I select "Student 2" conversation in messaging + And I click on "Hi!" "group_message_message_content" + And I should see "1" in the "[data-region='message-selected-court']" "css_element" + And I click on "Hello!" "group_message_message_content" + And I should see "2" in the "[data-region='message-selected-court']" "css_element" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" +# Deleting, so messages should not be there + And I should see "Delete" + And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element" + Then I should not see "Delete" + And I should not see "Hi!" + And I should not see "Hello!" in the "Student 2" "group_message_conversation" + And I should see "##today##j F##" in the "Student 2" "group_message_conversation" + And I should see "Are you free?" in the "Student 2" "group_message_conversation" + And I should not see "Messages selected" +# Check messages were not deleted for the other user + And I log out + And I log in as "student2" + And I open messaging + And I open the "Private" conversations list + And I select "Student 1" conversation in messaging + And I should see "Hi!" + And I should see "Hello!" + And I should see "Are you free?" + + Scenario: Cancel deleting two messages from a private conversation + Given I log in as "student1" + And I open messaging + And I should see "Private" + And I open the "Private" conversations list + And I should see "Student 2" + And I select "Student 2" conversation in messaging + And I click on "Hi!" "group_message_message_content" + And I click on "Hello!" "group_message_message_content" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" +# Canceling deletion, so messages should be there + And I should see "Cancel" + And I click on "//button[@data-action='cancel-confirm']" "xpath_element" + Then I should not see "Cancel" + And I should see "Hi!" + And I should see "Hello!" in the "Student 2" "group_message_conversation" + And I should see "2" in the "[data-region='message-selected-court']" "css_element" + + Scenario: Delete a message sent by the user from a favorite conversation + Given the following "favourite conversations" exist: + | user | contact | + | student1 | student2 | + And I log in as "student1" + And I open messaging + And I should see "Student 2" + And I select "Student 2" conversation in messaging + And I click on "Hi!" "group_message_message_content" + And I should see "1" in the "[data-region='message-selected-court']" "css_element" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" +# Deleting, so messages should not be there + And I should see "Delete" + And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element" + Then I should not see "Delete" + And I should not see "Hi!" + And I should see "##today##j F##" in the "Student 2" "group_message_conversation" + And I should see "Hello!" in the "Student 2" "group_message_conversation" + And I should not see "Messages selected" + + Scenario: Delete two messages from a favourite conversation; one sent by another user + Given the following "favourite conversations" exist: + | user | contact | + | student1 | student2 | + And I log in as "student1" + And I open messaging + And I should see "Student 2" + And I select "Student 2" conversation in messaging + And I click on "Hi!" "group_message_message_content" + And I should see "1" in the "[data-region='message-selected-court']" "css_element" + And I click on "Hello!" "group_message_message_content" + And I should see "2" in the "[data-region='message-selected-court']" "css_element" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" +# Deleting, so messages should not be there + And I should see "Delete" + And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element" + Then I should not see "Delete" + And I should not see "Hi!" + And I should not see "Hello!" in the "Student 2" "group_message_conversation" + And I should see "##today##j F##" in the "Student 2" "group_message_conversation" + And I should see "Are you free?" in the "Student 2" "group_message_conversation" + And I should not see "Messages selected" + + Scenario: Cancel deleting two messages from a favourite conversation + Given the following "favourite conversations" exist: + | user | contact | + | student1 | student2 | + And I log in as "student1" + And I open messaging + And I should see "Student 2" + And I select "Student 2" conversation in messaging + And I click on "Hi!" "group_message_message_content" + And I click on "Hello!" "group_message_message_content" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" +# Canceling deletion, so messages should be there + And I should see "Cancel" + And I click on "//button[@data-action='cancel-confirm']" "xpath_element" + Then I should not see "Cancel" + And I should see "Hi!" + And I should see "Hello!" in the "Student 2" "group_message_conversation" + And I should see "2" in the "[data-region='message-selected-court']" "css_element" + + Scenario: Check an empty favourite conversation is still favourite + Given the following "favourite conversations" exist: + | user | contact | + | student1 | student2 | + And I log in as "student1" + And I open messaging + And I should see "Student 2" + And I select "Student 2" conversation in the "favourites" conversations list + And I click on "Hi!" "group_message_message_content" + And I click on "Hello!" "group_message_message_content" + And I click on "Are you free?" "group_message_message_content" + And "Delete selected messages" "button" should exist + When I click on "Delete selected messages" "button" + And I should see "Delete" + And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element" + And I go back in "view-conversation" message drawer + Then I should not see "Student 2" in the "//*[@data-region='message-drawer']//div[@data-region='view-overview-favourites']" "xpath_element" + And I send "Hi!" message to "Student 2" user + And I go back in "view-conversation" message drawer + And I go back in "view-search" message drawer + And I open the "Starred" conversations list + And I should see "Student 2" in the "//*[@data-region='message-drawer']//div[@data-region='view-overview-favourites']" "xpath_element"