weekly release 4.0dev
[moodle.git] / mod / forum / amd / src / grades / expandconversation.js
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * This module handles the creation of a Modal that shows the user's post in context of the entire discussion.
18  *
19  * @module     mod_forum/grades/expandconversation
20  * @package    mod_forum
21  * @copyright  2019 Mathew May <mathew.solutions>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 import * as ForumSelectors from './grader/selectors';
25 import Repository from 'mod_forum/repository';
26 import {exception as showException} from "core/notification";
27 import Templates from 'core/templates';
28 import * as Modal from 'core/modal_factory';
29 import * as ModalEvents from 'core/modal_events';
31 /**
32  * Find the Node containing the gradable details from the provided node by searching up the tree.
33  *
34  * @param {HTMLElement} node
35  * @returns {HTMLElement}
36  */
37 const findGradableNode = node => node.closest(ForumSelectors.expandConversation);
39 /**
40  * Show the post in context in a modal.
41  *
42  * @param {HTMLElement} rootNode The button that has been clicked
43  */
44 const showPostInContext = async(rootNode, {
45     focusOnClose = null,
46 } = {}) => {
47     const postId = rootNode.dataset.postid;
48     const discussionId = rootNode.dataset.discussionid;
49     const discussionName = rootNode.dataset.name;
50     const experimentalDisplayMode = rootNode.dataset.experimentalDisplayMode == "1";
52     const [
53         allPosts,
54         modal,
55     ] = await Promise.all([
56         Repository.getDiscussionPosts(parseInt(discussionId)),
57         Modal.create({
58             title: discussionName,
59             large: true,
60             type: Modal.types.CANCEL
61         }),
62     ]);
64     const postsById = new Map(allPosts.posts.map(post => {
65         post.readonly = true;
66         post.hasreplies = false;
67         post.replies = [];
68         return [post.id, post];
69     }));
71     let posts = [];
72     allPosts.posts.forEach(post => {
73         if (post.parentid) {
74             const parent = postsById.get(post.parentid);
75             if (parent) {
76                 post.parentauthorname = parent.author.fullname;
77                 parent.hasreplies = true;
78                 parent.replies.push(post);
79             } else {
80                 posts.push(post);
81             }
82         } else {
83             posts.push(post);
84         }
85     });
87     // Handle hidden event.
88     modal.getRoot().on(ModalEvents.hidden, function() {
89         // Destroy when hidden.
90         modal.destroy();
91         try {
92             focusOnClose.focus();
93         } catch (e) {
94             // eslint-disable-line
95         }
96     });
98     modal.getRoot().on(ModalEvents.bodyRendered, () => {
99         const relevantPost = modal.getRoot()[0].querySelector(`#p${postId}`);
100         if (relevantPost) {
101             relevantPost.scrollIntoView({behavior: "smooth"});
102         }
103     });
105     modal.show();
107     // Note: We do not use await here because it messes with the Modal transitions.
108     const templatePromise = Templates.render('mod_forum/grades/grader/discussion/post_modal', {
109         posts,
110         experimentaldisplaymode: experimentalDisplayMode
111     });
112     modal.setBody(templatePromise);
113 };
115 /**
116  * Register event listeners for the expand conversations button.
117  *
118  * @param {HTMLElement} rootNode The root to listen to.
119  */
120 export const registerEventListeners = (rootNode) => {
121     rootNode.addEventListener('click', (e) => {
122         const rootNode = findGradableNode(e.target);
124         if (rootNode) {
125             e.preventDefault();
127             try {
128                 showPostInContext(rootNode, {
129                     focusOnClose: e.target,
130                 });
131             } catch (err) {
132                 showException(err);
133             }
134         }
135     });
136 };