MDL-64821 mod_forum: add show/hide reply toggle for modern view

This commit is contained in:
Ryan Wyllie 2019-08-28 15:27:10 +08:00
parent 38d96b6540
commit 7902e4dc55
11 changed files with 295 additions and 54 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
define ("mod_forum/inpage_reply",["jquery","core/templates","core/notification","mod_forum/repository","mod_forum/selectors"],function(a,b,c,d,f){var g={MODERN:4,THREADED:2,NESTED:3,FLAT_OLDEST_FIRST:1,FLAT_NEWEST_FIRST:-1},h={MOODLE:0},i=function(a){var b=a.find(f.post.inpageSubmitBtnText),c=a.find(f.post.loadingIconContainer),d=a.outerWidth();a.css("width",d);b.addClass("hidden");c.removeClass("hidden")},j=function(a){var b=a.find(f.post.inpageSubmitBtnText),c=a.find(f.post.loadingIconContainer);a.css("width","");b.removeClass("hidden");c.addClass("hidden")},k=function(k){k.on("click",f.post.inpageSubmitBtn,function(l){l.preventDefault();var e=a(l.currentTarget),m=e.parent().find(f.post.inpageReplyButton),n=e.parents(f.post.inpageReplyForm).get(0),o=n.elements.post.value.trim(),p=h.MOODLE,q=n.elements.reply.value,r=n.elements.subject.value,s=e.closest(f.post.post),t=n.elements.privatereply!=void 0?n.elements.privatereply.checked:!1,u=k.find(f.post.modeSelect),v=u.length?parseInt(u.get(0).value):null,w;if(o.length){i(e);m.prop("disabled",!0);d.addDiscussionPost(q,r,o,p,t,!0).then(function(a){var b=a.messages.reduce(function(a,b){if("success"==b.type){a+="<p>"+b.message+"</p>"}return a},"");c.addNotification({message:b,type:"success"});return a}).then(function(a){n.reset();var c=a.post;w=c.id;switch(v){case g.MODERN:var d=c.capabilities;c.showactionmenu=d.controlreadstatus||d.edit||d.split||d.delete||d.export||c.urls.viewparent;return b.render("mod_forum/forum_discussion_modern_post_reply",c);case g.THREADED:return b.render("mod_forum/forum_discussion_threaded_post",c);case g.NESTED:return b.render("mod_forum/forum_discussion_nested_post",c);default:return b.render("mod_forum/forum_discussion_post",c);}}).then(function(a,c){var d=s.find(f.post.repliesContainer).first();if(v==g.FLAT_NEWEST_FIRST){return b.prependNodeContents(d,a,c)}else{return b.appendNodeContents(d,a,c)}}).then(function(){j(e);m.prop("disabled",!1);return s.find(f.post.inpageReplyContent).hide()}).then(function(){location.href="#p"+w}).catch(function(a){j(e);m.prop("disabled",!1);return c.exception(a)})}})};return{init:function init(a){k(a)},CONTENT_FORMATS:h}});
define ("mod_forum/inpage_reply",["jquery","core/templates","core/notification","mod_forum/repository","mod_forum/selectors"],function(a,b,c,d,f){var g={MODERN:4,THREADED:2,NESTED:3,FLAT_OLDEST_FIRST:1,FLAT_NEWEST_FIRST:-1},h={POST_CREATED:"mod_forum-post-created"},i={MOODLE:0},j=function(a){var b=a.find(f.post.inpageSubmitBtnText),c=a.find(f.post.loadingIconContainer),d=a.outerWidth();a.css("width",d);b.addClass("hidden");c.removeClass("hidden")},k=function(a){var b=a.find(f.post.inpageSubmitBtnText),c=a.find(f.post.loadingIconContainer);a.css("width","");b.removeClass("hidden");c.addClass("hidden")},l=function(l){l.on("click",f.post.inpageSubmitBtn,function(m){m.preventDefault();var e=a(m.currentTarget),n=e.parent().find(f.post.inpageReplyButton),o=e.parents(f.post.inpageReplyForm).get(0),p=o.elements.post.value.trim(),q=i.MOODLE,r=o.elements.reply.value,s=o.elements.subject.value,t=e.closest(f.post.post),u=o.elements.privatereply!=void 0?o.elements.privatereply.checked:!1,v=l.find(f.post.modeSelect),w=v.length?parseInt(v.get(0).value):null,x;if(p.length){j(e);n.prop("disabled",!0);d.addDiscussionPost(r,s,p,q,u,!0).then(function(a){var b=a.messages.reduce(function(a,b){if("success"==b.type){a+="<p>"+b.message+"</p>"}return a},"");c.addNotification({message:b,type:"success"});return a}).then(function(a){o.reset();var c=a.post;x=c.id;switch(w){case g.MODERN:var d=c.capabilities;c.showactionmenu=d.controlreadstatus||d.edit||d.split||d.delete||d.export||c.urls.viewparent;return b.render("mod_forum/forum_discussion_modern_post_reply",c);case g.THREADED:return b.render("mod_forum/forum_discussion_threaded_post",c);case g.NESTED:return b.render("mod_forum/forum_discussion_nested_post",c);default:return b.render("mod_forum/forum_discussion_post",c);}}).then(function(a,c){var d=t.find(f.post.repliesContainer).first();if(w==g.FLAT_NEWEST_FIRST){return b.prependNodeContents(d,a,c)}else{return b.appendNodeContents(d,a,c)}}).then(function(){e.trigger(h.POST_CREATED,x);k(e);n.prop("disabled",!1);return t.find(f.post.inpageReplyContent).hide()}).then(function(){location.href="#p"+x}).catch(function(a){k(e);n.prop("disabled",!1);return c.exception(a)})}})};return{init:function init(a){l(a)},CONTENT_FORMATS:i,EVENTS:h}});
//# sourceMappingURL=inpage_reply.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
define ("mod_forum/selectors",[],function(){return{subscription:{toggle:"[data-type='subscription-toggle'][data-action='toggle']"},summary:{actions:"[data-container='discussion-summary-actions']"},post:{post:"[data-region=\"post\"]",action:"[data-region=\"post-action\"]",actionsContainer:"[data-region=\"post-actions-container\"]",authorName:"[data-region=\"author-name\"]",forumCoreContent:"[data-region-content='forum-post-core']",forumContent:"[data-content='forum-post']",forumSubject:"[data-region-content='forum-post-core-subject']",inpageReplyButton:"button",inpageReplyLink:"[data-action='collapsible-link']",inpageReplyCancelButton:"[data-action='cancel-inpage-reply']",inpageReplyCreateButton:"[data-action='create-inpage-reply']",inpageReplyContainer:"[data-region=\"inpage-reply-container\"]",inpageReplyContent:"[data-content='inpage-reply-content']",inpageReplyForm:"form[data-content='inpage-reply-form']",inpageSubmitBtn:"[data-action='forum-inpage-submit']",inpageSubmitBtnText:"[data-region='submit-text']",loadingIconContainer:"[data-region='loading-icon-container']",repliesContainer:"[data-region='replies-container']",modeSelect:"select[name='mode']"},lock:{toggle:"[data-action='toggle'][data-type='lock-toggle']",icon:"[data-region='locked-icon']"},favourite:{toggle:"[data-type='favorite-toggle'][data-action='toggle']"},pin:{toggle:"[data-type='pin-toggle'][data-action='toggle']"}}});
define ("mod_forum/selectors",[],function(){return{subscription:{toggle:"[data-type='subscription-toggle'][data-action='toggle']"},summary:{actions:"[data-container='discussion-summary-actions']"},post:{post:"[data-region=\"post\"]",action:"[data-region=\"post-action\"]",actionsContainer:"[data-region=\"post-actions-container\"]",authorName:"[data-region=\"author-name\"]",forumCoreContent:"[data-region-content='forum-post-core']",forumContent:"[data-content='forum-post']",forumSubject:"[data-region-content='forum-post-core-subject']",inpageReplyButton:"button",inpageReplyLink:"[data-action='collapsible-link']",inpageReplyCancelButton:"[data-action='cancel-inpage-reply']",inpageReplyCreateButton:"[data-action='create-inpage-reply']",inpageReplyContainer:"[data-region=\"inpage-reply-container\"]",inpageReplyContent:"[data-content='inpage-reply-content']",inpageReplyForm:"form[data-content='inpage-reply-form']",inpageSubmitBtn:"[data-action='forum-inpage-submit']",inpageSubmitBtnText:"[data-region='submit-text']",loadingIconContainer:"[data-region='loading-icon-container']",repliesContainer:"[data-region='replies-container']",replyCount:"[data-region=\"reply-count\"]",modeSelect:"select[name='mode']",showReplies:"[data-action=\"show-replies\"]",hideReplies:"[data-action=\"hide-replies\"]",repliesVisibilityToggleContainer:"[data-region=\"replies-visibility-toggle-container\"]"},lock:{toggle:"[data-action='toggle'][data-type='lock-toggle']",icon:"[data-region='locked-icon']"},favourite:{toggle:"[data-type='favorite-toggle'][data-action='toggle']"},pin:{toggle:"[data-type='pin-toggle'][data-action='toggle']"},discussion:{tools:"[data-container=\"discussion-tools\"]"}}});
//# sourceMappingURL=selectors.min.js.map

View File

@ -1 +1 @@
{"version":3,"sources":["../src/selectors.js"],"names":["define","subscription","toggle","summary","actions","post","action","actionsContainer","authorName","forumCoreContent","forumContent","forumSubject","inpageReplyButton","inpageReplyLink","inpageReplyCancelButton","inpageReplyCreateButton","inpageReplyContainer","inpageReplyContent","inpageReplyForm","inpageSubmitBtn","inpageSubmitBtnText","loadingIconContainer","repliesContainer","modeSelect","lock","icon","favourite","pin"],"mappings":"AAuBAA,OAAM,uBAAC,EAAD,CAAK,UAAW,CAClB,MAAO,CACHC,YAAY,CAAE,CACVC,MAAM,CAAE,yDADE,CADX,CAIHC,OAAO,CAAE,CACLC,OAAO,CAAE,+CADJ,CAJN,CAOHC,IAAI,CAAE,CACFA,IAAI,CAAE,wBADJ,CAEFC,MAAM,CAAE,+BAFN,CAGFC,gBAAgB,CAAE,0CAHhB,CAIFC,UAAU,CAAE,+BAJV,CAKFC,gBAAgB,CAAE,yCALhB,CAMFC,YAAY,CAAE,6BANZ,CAOFC,YAAY,CAAE,iDAPZ,CAQFC,iBAAiB,CAAE,QARjB,CASFC,eAAe,CAAE,kCATf,CAUFC,uBAAuB,CAAE,qCAVvB,CAWFC,uBAAuB,CAAE,qCAXvB,CAYFC,oBAAoB,CAAE,0CAZpB,CAaFC,kBAAkB,CAAE,uCAblB,CAcFC,eAAe,CAAE,wCAdf,CAeFC,eAAe,CAAE,qCAff,CAgBFC,mBAAmB,CAAE,6BAhBnB,CAiBFC,oBAAoB,CAAE,wCAjBpB,CAkBFC,gBAAgB,CAAE,mCAlBhB,CAmBFC,UAAU,CAAE,qBAnBV,CAPH,CA4BHC,IAAI,CAAE,CACFtB,MAAM,CAAE,iDADN,CAEFuB,IAAI,CAAE,6BAFJ,CA5BH,CAgCHC,SAAS,CAAE,CACPxB,MAAM,CAAE,qDADD,CAhCR,CAmCHyB,GAAG,CAAE,CACDzB,MAAM,CAAE,gDADP,CAnCF,CAuCV,CAxCK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Common CSS selectors for the forum UI.\n *\n * @module mod_forum/selectors\n * @package mod_forum\n * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n subscription: {\n toggle: \"[data-type='subscription-toggle'][data-action='toggle']\",\n },\n summary: {\n actions: \"[data-container='discussion-summary-actions']\"\n },\n post: {\n post: '[data-region=\"post\"]',\n action: '[data-region=\"post-action\"]',\n actionsContainer: '[data-region=\"post-actions-container\"]',\n authorName: '[data-region=\"author-name\"]',\n forumCoreContent: \"[data-region-content='forum-post-core']\",\n forumContent: \"[data-content='forum-post']\",\n forumSubject: \"[data-region-content='forum-post-core-subject']\",\n inpageReplyButton: \"button\",\n inpageReplyLink: \"[data-action='collapsible-link']\",\n inpageReplyCancelButton: \"[data-action='cancel-inpage-reply']\",\n inpageReplyCreateButton: \"[data-action='create-inpage-reply']\",\n inpageReplyContainer: '[data-region=\"inpage-reply-container\"]',\n inpageReplyContent: \"[data-content='inpage-reply-content']\",\n inpageReplyForm: \"form[data-content='inpage-reply-form']\",\n inpageSubmitBtn: \"[data-action='forum-inpage-submit']\",\n inpageSubmitBtnText: \"[data-region='submit-text']\",\n loadingIconContainer: \"[data-region='loading-icon-container']\",\n repliesContainer: \"[data-region='replies-container']\",\n modeSelect: \"select[name='mode']\"\n },\n lock: {\n toggle: \"[data-action='toggle'][data-type='lock-toggle']\",\n icon: \"[data-region='locked-icon']\"\n },\n favourite: {\n toggle: \"[data-type='favorite-toggle'][data-action='toggle']\",\n },\n pin: {\n toggle: \"[data-type='pin-toggle'][data-action='toggle']\",\n },\n };\n});\n"],"file":"selectors.min.js"}
{"version":3,"sources":["../src/selectors.js"],"names":["define","subscription","toggle","summary","actions","post","action","actionsContainer","authorName","forumCoreContent","forumContent","forumSubject","inpageReplyButton","inpageReplyLink","inpageReplyCancelButton","inpageReplyCreateButton","inpageReplyContainer","inpageReplyContent","inpageReplyForm","inpageSubmitBtn","inpageSubmitBtnText","loadingIconContainer","repliesContainer","replyCount","modeSelect","showReplies","hideReplies","repliesVisibilityToggleContainer","lock","icon","favourite","pin","discussion","tools"],"mappings":"AAuBAA,OAAM,uBAAC,EAAD,CAAK,UAAW,CAClB,MAAO,CACHC,YAAY,CAAE,CACVC,MAAM,CAAE,yDADE,CADX,CAIHC,OAAO,CAAE,CACLC,OAAO,CAAE,+CADJ,CAJN,CAOHC,IAAI,CAAE,CACFA,IAAI,CAAE,wBADJ,CAEFC,MAAM,CAAE,+BAFN,CAGFC,gBAAgB,CAAE,0CAHhB,CAIFC,UAAU,CAAE,+BAJV,CAKFC,gBAAgB,CAAE,yCALhB,CAMFC,YAAY,CAAE,6BANZ,CAOFC,YAAY,CAAE,iDAPZ,CAQFC,iBAAiB,CAAE,QARjB,CASFC,eAAe,CAAE,kCATf,CAUFC,uBAAuB,CAAE,qCAVvB,CAWFC,uBAAuB,CAAE,qCAXvB,CAYFC,oBAAoB,CAAE,0CAZpB,CAaFC,kBAAkB,CAAE,uCAblB,CAcFC,eAAe,CAAE,wCAdf,CAeFC,eAAe,CAAE,qCAff,CAgBFC,mBAAmB,CAAE,6BAhBnB,CAiBFC,oBAAoB,CAAE,wCAjBpB,CAkBFC,gBAAgB,CAAE,mCAlBhB,CAmBFC,UAAU,CAAE,+BAnBV,CAoBFC,UAAU,CAAE,qBApBV,CAqBFC,WAAW,CAAE,gCArBX,CAsBFC,WAAW,CAAE,gCAtBX,CAuBFC,gCAAgC,CAAE,uDAvBhC,CAPH,CAgCHC,IAAI,CAAE,CACF1B,MAAM,CAAE,iDADN,CAEF2B,IAAI,CAAE,6BAFJ,CAhCH,CAoCHC,SAAS,CAAE,CACP5B,MAAM,CAAE,qDADD,CApCR,CAuCH6B,GAAG,CAAE,CACD7B,MAAM,CAAE,gDADP,CAvCF,CA0CH8B,UAAU,CAAE,CACRC,KAAK,CAAE,uCADC,CA1CT,CA8CV,CA/CK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Common CSS selectors for the forum UI.\n *\n * @module mod_forum/selectors\n * @package mod_forum\n * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n subscription: {\n toggle: \"[data-type='subscription-toggle'][data-action='toggle']\",\n },\n summary: {\n actions: \"[data-container='discussion-summary-actions']\"\n },\n post: {\n post: '[data-region=\"post\"]',\n action: '[data-region=\"post-action\"]',\n actionsContainer: '[data-region=\"post-actions-container\"]',\n authorName: '[data-region=\"author-name\"]',\n forumCoreContent: \"[data-region-content='forum-post-core']\",\n forumContent: \"[data-content='forum-post']\",\n forumSubject: \"[data-region-content='forum-post-core-subject']\",\n inpageReplyButton: \"button\",\n inpageReplyLink: \"[data-action='collapsible-link']\",\n inpageReplyCancelButton: \"[data-action='cancel-inpage-reply']\",\n inpageReplyCreateButton: \"[data-action='create-inpage-reply']\",\n inpageReplyContainer: '[data-region=\"inpage-reply-container\"]',\n inpageReplyContent: \"[data-content='inpage-reply-content']\",\n inpageReplyForm: \"form[data-content='inpage-reply-form']\",\n inpageSubmitBtn: \"[data-action='forum-inpage-submit']\",\n inpageSubmitBtnText: \"[data-region='submit-text']\",\n loadingIconContainer: \"[data-region='loading-icon-container']\",\n repliesContainer: \"[data-region='replies-container']\",\n replyCount: '[data-region=\"reply-count\"]',\n modeSelect: \"select[name='mode']\",\n showReplies: '[data-action=\"show-replies\"]',\n hideReplies: '[data-action=\"hide-replies\"]',\n repliesVisibilityToggleContainer: '[data-region=\"replies-visibility-toggle-container\"]'\n },\n lock: {\n toggle: \"[data-action='toggle'][data-type='lock-toggle']\",\n icon: \"[data-region='locked-icon']\"\n },\n favourite: {\n toggle: \"[data-type='favorite-toggle'][data-action='toggle']\",\n },\n pin: {\n toggle: \"[data-type='pin-toggle'][data-action='toggle']\",\n },\n discussion: {\n tools: '[data-container=\"discussion-tools\"]'\n }\n };\n});\n"],"file":"selectors.min.js"}

View File

@ -43,6 +43,27 @@ const getPostContainer = (element) => {
return element.closest(Selectors.post.post);
};
/**
* Get the closest post container element from the given element.
*
* @param {Object} element jQuery element to search from
* @param {Number} id Id of the post to find.
* @return {Object} jQuery element
*/
const getPostContainerById = (element, id) => {
return element.find(`${Selectors.post.post}[data-post-id=${id}]`);
};
/**
* Get the parent post container elements from the given element.
*
* @param {Object} element jQuery element to search from
* @return {Object} jQuery element
*/
const getParentPostContainers = (element) => {
return element.parents(Selectors.post.post);
};
/**
* Get the post content container element from the post container element.
*
@ -64,36 +85,210 @@ const getInPageReplyContainer = (postContainer) => {
};
/**
* Show the in page reply form in the given in page reply container. The form
* display will be animated.
* Get the in page reply form element from the post container element.
*
* @param {Object} inPageReplyContainer jQuery element for the in page reply container
* @param {Function} afterAnimationCallback Callback after animation completes
* @param {Object} postContainer jQuery element for the post container
* @return {Object} jQuery element
*/
const showInPageReplyForm = (inPageReplyContainer, afterAnimationCallback) => {
const form = inPageReplyContainer.find(Selectors.post.inpageReplyContent);
const getInPageReplyForm = (postContainer) => {
return getInPageReplyContainer(postContainer).find(Selectors.post.inpageReplyContent);
};
form.slideDown({
/**
* Get the in page reply create (reply) button element from the post container element.
*
* @param {Object} postContainer jQuery element for the post container
* @return {Object} jQuery element
*/
const getInPageReplyCreateButton = (postContainer) => {
return getPostContentContainer(postContainer).find(Selectors.post.inpageReplyCreateButton);
};
/**
* Get the replies visibility toggle container (show/hide replies button container) element
* from the post container element.
*
* @param {Object} postContainer jQuery element for the post container
* @return {Object} jQuery element
*/
const getRepliesVisibilityToggleContainer = (postContainer) => {
return postContainer.children(Selectors.post.repliesVisibilityToggleContainer);
};
/**
* Get the replies container element from the post container element.
*
* @param {Object} postContainer jQuery element for the post container
* @return {Object} jQuery element
*/
const getRepliesContainer = (postContainer) => {
return postContainer.children(Selectors.post.repliesContainer);
};
/**
* Check if the post has any replies.
*
* @param {Object} postContainer jQuery element for the post container
* @return {Bool}
*/
const hasReplies = (postContainer) => {
return getRepliesContainer(postContainer).children().length > 0;
};
/**
* Get the show replies button element from the replies visibility toggle container element.
*
* @param {Object} replyVisibilityToggleContainer jQuery element for the toggle container
* @return {Object} jQuery element
*/
const getShowRepliesButton = (replyVisibilityToggleContainer) => {
return replyVisibilityToggleContainer.find(Selectors.post.showReplies);
};
/**
* Get the hide replies button element from the replies visibility toggle container element.
*
* @param {Object} replyVisibilityToggleContainer jQuery element for the toggle container
* @return {Object} jQuery element
*/
const getHideRepliesButton = (replyVisibilityToggleContainer) => {
return replyVisibilityToggleContainer.find(Selectors.post.hideReplies);
};
/**
* Check if the replies are visible.
*
* @param {Object} postContainer jQuery element for the post container
* @return {Bool}
*/
const repliesVisible = (postContainer) => {
const repliesContainer = getRepliesContainer(postContainer);
return repliesContainer.is(':visible');
};
/**
* Show the post replies.
*
* @param {Object} postContainer jQuery element for the post container
* @param {Number|null} postIdToSee Id of the post to scroll into view (if any)
*/
const showReplies = (postContainer, postIdToSee = null) => {
const repliesContainer = getRepliesContainer(postContainer);
const replyVisibilityToggleContainer = getRepliesVisibilityToggleContainer(postContainer);
const showButton = getShowRepliesButton(replyVisibilityToggleContainer);
const hideButton = getHideRepliesButton(replyVisibilityToggleContainer);
showButton.addClass('hidden');
hideButton.removeClass('hidden');
repliesContainer.slideDown({
duration: ANIMATION_DURATION,
queue: false,
complete: afterAnimationCallback
complete: () => {
if (postIdToSee) {
const postContainerToSee = getPostContainerById(repliesContainer, postIdToSee);
if (postContainerToSee.length) {
postContainerToSee[0].scrollIntoView();
}
}
}
}).css('display', 'none').fadeIn(ANIMATION_DURATION);
};
/**
* Hide the post replies.
*
* @param {Object} postContainer jQuery element for the post container
*/
const hideReplies = (postContainer) => {
const repliesContainer = getRepliesContainer(postContainer);
const replyVisibilityToggleContainer = getRepliesVisibilityToggleContainer(postContainer);
const showButton = getShowRepliesButton(replyVisibilityToggleContainer);
const hideButton = getHideRepliesButton(replyVisibilityToggleContainer);
showButton.removeClass('hidden');
hideButton.addClass('hidden');
repliesContainer.slideUp({
duration: ANIMATION_DURATION,
queue: false
}).fadeOut(ANIMATION_DURATION);
};
/** Variable to hold the showInPageReplyForm function after it's built. */
let showInPageReplyForm = null;
/**
* Build the showInPageReplyForm function with the given additional template context.
*
* @param {Object} additionalTemplateContext Additional render context for the in page reply template.
* @return {Function}
*/
const buildShowInPageReplyFormFunction = (additionalTemplateContext) => {
/**
* Show the in page reply form in the given in page reply container. The form
* display will be animated.
*
* @param {Object} postContainer jQuery element for the post container
*/
return async (postContainer) => {
const inPageReplyContainer = getInPageReplyContainer(postContainer);
const repliesVisibilityToggleContainer = getRepliesVisibilityToggleContainer(postContainer);
const inPageReplyCreateButton = getInPageReplyCreateButton(postContainer);
if (!hasInPageReplyForm(inPageReplyContainer)) {
try {
const html = await renderInPageReplyTemplate(additionalTemplateContext, inPageReplyCreateButton, postContainer);
Templates.appendNodeContents(inPageReplyContainer, html, '');
} catch (e) {
Notification.exception(e);
}
}
inPageReplyCreateButton.fadeOut(ANIMATION_DURATION, () => {
const inPageReplyForm = getInPageReplyForm(postContainer);
inPageReplyForm.slideDown({
duration: ANIMATION_DURATION,
queue: false,
complete: () => {
inPageReplyForm.find('textarea').focus();
}
}).css('display', 'none').fadeIn(ANIMATION_DURATION);
if (repliesVisibilityToggleContainer.length && hasReplies(postContainer)) {
repliesVisibilityToggleContainer.fadeIn(ANIMATION_DURATION);
hideReplies(postContainer);
}
});
};
};
/**
* Hide the in page reply form in the given in page reply container. The form
* display will be animated.
*
* @param {Object} inPageReplyContainer jQuery element for the in page reply container
* @param {Function} afterAnimationCallback Callback after animation completes
* @param {Object} postContainer jQuery element for the post container
* @param {Number|null} postIdToSee Id of the post to scroll into view (if any)
*/
const hideInPageReplyForm = (inPageReplyContainer, afterAnimationCallback) => {
const form = inPageReplyContainer.find(Selectors.post.inpageReplyContent);
const hideInPageReplyForm = (postContainer, postIdToSee = null) => {
const inPageReplyForm = getInPageReplyForm(postContainer);
const inPageReplyCreateButton = getInPageReplyCreateButton(postContainer);
const repliesVisibilityToggleContainer = getRepliesVisibilityToggleContainer(postContainer);
form.slideUp({
if (repliesVisibilityToggleContainer.length && hasReplies(postContainer)) {
repliesVisibilityToggleContainer.fadeOut(ANIMATION_DURATION);
if (!repliesVisible(postContainer)) {
showReplies(postContainer, postIdToSee);
}
}
inPageReplyForm.slideUp({
duration: ANIMATION_DURATION,
queue: false,
complete: afterAnimationCallback
complete: () => {
inPageReplyCreateButton.fadeIn(ANIMATION_DURATION);
}
}).fadeOut(200);
};
@ -133,52 +328,65 @@ const renderInPageReplyTemplate = (additionalTemplateContext, button, postContai
return Templates.render('mod_forum/inpage_reply_modern', context);
};
/**
* Increment the total reply count in the show/hide replies buttons for the post.
*
* @param {Object} postContainer jQuery element for the post container
*/
const incrementTotalReplyCount = (postContainer) => {
getRepliesVisibilityToggleContainer(postContainer).find(Selectors.post.replyCount).each((index, element) => {
const currentCount = parseInt(element.innerText, 10);
element.innerText = currentCount + 1;
});
};
/**
* Create all of the event listeners for the discussion.
*
* @param {Object} root jQuery element for the discussion container
* @param {Object} additionalTemplateContext Additional render context for the in page reply template
*/
const registerEventListeners = (root, additionalTemplateContext) => {
const registerEventListeners = (root) => {
CustomEvents.define(root, [CustomEvents.events.activate]);
// Auto expanding text area for in page reply.
AutoRows.init(root);
// Reply button is clicked.
root.on(CustomEvents.events.activate, Selectors.post.inpageReplyCreateButton, async (e, data) => {
root.on(CustomEvents.events.activate, Selectors.post.inpageReplyCreateButton, (e, data) => {
data.originalEvent.preventDefault();
const button = $(e.currentTarget);
const postContainer = getPostContainer(button);
const inPageReplyContainer = getInPageReplyContainer(postContainer);
if (!hasInPageReplyForm(inPageReplyContainer)) {
try {
const html = await renderInPageReplyTemplate(additionalTemplateContext, button, postContainer);
Templates.appendNodeContents(inPageReplyContainer, html, '');
} catch (e) {
Notification.exception(e);
}
}
button.fadeOut(ANIMATION_DURATION, () => {
showInPageReplyForm(inPageReplyContainer, () => {
inPageReplyContainer.find(Selectors.post.inpageReplyContent).find('textarea').focus();
});
});
const postContainer = getPostContainer($(e.currentTarget));
showInPageReplyForm(postContainer);
});
// Cancel in page reply button.
root.on(CustomEvents.events.activate, Selectors.post.inpageReplyCancelButton, (e, data) => {
data.originalEvent.preventDefault();
const postContainer = getPostContainer($(e.currentTarget));
hideInPageReplyForm(postContainer);
});
const button = $(e.currentTarget);
const postContainer = getPostContainer(button);
const postContentContainer = getPostContentContainer(postContainer);
const inPageReplyContainer = getInPageReplyContainer(postContainer);
const inPageReplyCreateButton = postContentContainer.find(Selectors.post.inpageReplyCreateButton);
hideInPageReplyForm(inPageReplyContainer, () => {
inPageReplyCreateButton.fadeIn(ANIMATION_DURATION);
// Show replies button clicked.
root.on(CustomEvents.events.activate, Selectors.post.showReplies, (e, data) => {
data.originalEvent.preventDefault();
const postContainer = getPostContainer($(e.target));
showReplies(postContainer);
});
// Hide replies button clicked.
root.on(CustomEvents.events.activate, Selectors.post.hideReplies, (e, data) => {
data.originalEvent.preventDefault();
const postContainer = getPostContainer($(e.target));
hideReplies(postContainer);
});
// Post created with in page reply.
root.on(InPageReply.EVENTS.POST_CREATED, Selectors.post.inpageSubmitBtn, (e, newPostId) => {
const currentTarget = $(e.currentTarget);
const postContainer = getPostContainer(currentTarget);
const postContainers = getParentPostContainers(currentTarget);
hideInPageReplyForm(postContainer, newPostId);
postContainers.each((index, container) => {
incrementTotalReplyCount($(container));
});
});
};
@ -190,15 +398,17 @@ const registerEventListeners = (root, additionalTemplateContext) => {
* @param {Object} context Additional render context for the in page reply template
*/
export const init = (root, context) => {
// Build the showInPageReplyForm function with the additional render context.
showInPageReplyForm = buildShowInPageReplyFormFunction(context);
// Add discussion event listeners.
registerEventListeners(root, context);
// Add default discussion javascript (keyboard nav etc).
registerEventListeners(root);
// Initialise default discussion javascript (keyboard nav etc).
Discussion.init(root);
// Add in page reply javascript.
InPageReply.init(root);
// Initialise the settings menu javascript.
const discussionToolsContainer = root.find('[data-container="discussion-tools"]');
const discussionToolsContainer = root.find(Selectors.discussion.tools);
LockToggle.init(discussionToolsContainer);
FavouriteToggle.init(discussionToolsContainer);
Pin.init(discussionToolsContainer);

View File

@ -43,6 +43,10 @@ define([
FLAT_NEWEST_FIRST: -1
};
var EVENTS = {
POST_CREATED: 'mod_forum-post-created'
};
/**
* Moodle formats taken from the FORMAT_* constants declared in lib/weblib.php.
* @type {Object}
@ -156,6 +160,7 @@ define([
}
})
.then(function() {
submitButton.trigger(EVENTS.POST_CREATED, newid);
hideSubmitButtonLoadingIcon(submitButton);
allButtons.prop('disabled', false);
return currentRoot.find(Selectors.post.inpageReplyContent).hide();
@ -177,6 +182,7 @@ define([
init: function(root) {
registerEventListeners(root);
},
CONTENT_FORMATS: CONTENT_FORMATS
CONTENT_FORMATS: CONTENT_FORMATS,
EVENTS: EVENTS
};
});

View File

@ -48,7 +48,11 @@ define([], function() {
inpageSubmitBtnText: "[data-region='submit-text']",
loadingIconContainer: "[data-region='loading-icon-container']",
repliesContainer: "[data-region='replies-container']",
modeSelect: "select[name='mode']"
replyCount: '[data-region="reply-count"]',
modeSelect: "select[name='mode']",
showReplies: '[data-action="show-replies"]',
hideReplies: '[data-action="hide-replies"]',
repliesVisibilityToggleContainer: '[data-region="replies-visibility-toggle-container"]'
},
lock: {
toggle: "[data-action='toggle'][data-type='lock-toggle']",
@ -60,5 +64,8 @@ define([], function() {
pin: {
toggle: "[data-type='pin-toggle'][data-action='toggle']",
},
discussion: {
tools: '[data-container="discussion-tools"]'
}
};
});

View File

@ -311,6 +311,7 @@ $string['forum:viewsubscribers'] = 'View subscribers';
$string['generalforum'] = 'Standard forum for general use';
$string['generalforums'] = 'General forums';
$string['hiddenforumpost'] = 'Hidden forum post';
$string['hidepreviousrepliescount'] = 'Hide previous replies ({$a})';
$string['indicator:cognitivedepth'] = 'Forum cognitive';
$string['indicator:cognitivedepth_help'] = 'This indicator is based on the cognitive depth reached by the student in a Forum activity.';
$string['indicator:socialbreadth'] = 'Forum social';
@ -598,6 +599,7 @@ $string['seeallposts'] = 'See all posts made by this user';
$string['settings'] = 'Settings';
$string['shortpost'] = 'Short post';
$string['showingcountoftotaldiscussions'] = 'Showing {$a->count} of {$a->total} discussions';
$string['showpreviousrepliescount'] = 'Show previous replies ({$a})';
$string['showsubscribers'] = 'Show/edit current subscribers';
$string['singleforum'] = 'A single simple discussion';
$string['smallmessage'] = '{$a->user} posted in {$a->forumname}';

View File

@ -53,6 +53,22 @@
{{/isdeleted}}
{{/footer}}
{{$replies}}
<div class="indent my-4" data-region="replies-visibility-toggle-container" style="display: none">
<button class="btn btn-link pl-0" data-action="show-replies">
{{#str}}
showpreviousrepliescount,
mod_forum,
<span data-region="reply-count">{{#totalreplycount}}{{.}}{{/totalreplycount}}{{^totalreplycount}}0{{/totalreplycount}}</span>
{{/str}}
</button>
<button class="btn btn-link hidden pl-0" data-action="hide-replies">
{{#str}}
hidepreviousrepliescount,
mod_forum,
<span data-region="reply-count">{{#totalreplycount}}{{.}}{{/totalreplycount}}{{^totalreplycount}}0{{/totalreplycount}}</span>
{{/str}}
</button>
</div>
<div class="indent replies-container" data-region="replies-container">
{{#hasreplies}}
{{#replies}}