weekly release 4.0dev
[moodle.git] / mod / forum / amd / src / grades / expandconversation.js
CommitLineData
3d58c1be
MM
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/>.
15
16/**
4ab14bbb 17 * This module handles the creation of a Modal that shows the user's post in context of the entire discussion.
3d58c1be
MM
18 *
19 * @module mod_forum/grades/expandconversation
3d58c1be
MM
20 * @copyright 2019 Mathew May <mathew.solutions>
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 */
23import * as ForumSelectors from './grader/selectors';
24import Repository from 'mod_forum/repository';
25import {exception as showException} from "core/notification";
26import Templates from 'core/templates';
27import * as Modal from 'core/modal_factory';
28import * as ModalEvents from 'core/modal_events';
29
30/**
31 * Find the Node containing the gradable details from the provided node by searching up the tree.
32 *
33 * @param {HTMLElement} node
34 * @returns {HTMLElement}
35 */
36const findGradableNode = node => node.closest(ForumSelectors.expandConversation);
37
38/**
39 * Show the post in context in a modal.
40 *
4ab14bbb 41 * @param {HTMLElement} rootNode The button that has been clicked
3d58c1be 42 */
21900f03
AN
43const showPostInContext = async(rootNode, {
44 focusOnClose = null,
45} = {}) => {
3d58c1be
MM
46 const postId = rootNode.dataset.postid;
47 const discussionId = rootNode.dataset.discussionid;
48 const discussionName = rootNode.dataset.name;
69848e71 49 const experimentalDisplayMode = rootNode.dataset.experimentalDisplayMode == "1";
3d58c1be
MM
50
51 const [
52 allPosts,
53 modal,
54 ] = await Promise.all([
55 Repository.getDiscussionPosts(parseInt(discussionId)),
56 Modal.create({
57 title: discussionName,
58 large: true,
59 type: Modal.types.CANCEL
60 }),
61 ]);
62
035bd996 63 const postsById = new Map(allPosts.posts.map(post => {
4ab14bbb 64 post.readonly = true;
035bd996
RW
65 post.hasreplies = false;
66 post.replies = [];
67 return [post.id, post];
68 }));
4ab14bbb 69
035bd996
RW
70 let posts = [];
71 allPosts.posts.forEach(post => {
72 if (post.parentid) {
73 const parent = postsById.get(post.parentid);
74 if (parent) {
69848e71 75 post.parentauthorname = parent.author.fullname;
035bd996
RW
76 parent.hasreplies = true;
77 parent.replies.push(post);
78 } else {
79 posts.push(post);
80 }
81 } else {
82 posts.push(post);
83 }
4ab14bbb
MM
84 });
85
3d58c1be
MM
86 // Handle hidden event.
87 modal.getRoot().on(ModalEvents.hidden, function() {
88 // Destroy when hidden.
89 modal.destroy();
21900f03
AN
90 try {
91 focusOnClose.focus();
92 } catch (e) {
93 // eslint-disable-line
94 }
3d58c1be
MM
95 });
96
035bd996 97 modal.getRoot().on(ModalEvents.bodyRendered, () => {
3d58c1be
MM
98 const relevantPost = modal.getRoot()[0].querySelector(`#p${postId}`);
99 if (relevantPost) {
100 relevantPost.scrollIntoView({behavior: "smooth"});
101 }
3d58c1be 102 });
035bd996
RW
103
104 modal.show();
105
106 // Note: We do not use await here because it messes with the Modal transitions.
69848e71
RW
107 const templatePromise = Templates.render('mod_forum/grades/grader/discussion/post_modal', {
108 posts,
109 experimentaldisplaymode: experimentalDisplayMode
110 });
035bd996 111 modal.setBody(templatePromise);
3d58c1be
MM
112};
113
114/**
115 * Register event listeners for the expand conversations button.
116 *
117 * @param {HTMLElement} rootNode The root to listen to.
118 */
4ab14bbb
MM
119export const registerEventListeners = (rootNode) => {
120 rootNode.addEventListener('click', (e) => {
3d58c1be
MM
121 const rootNode = findGradableNode(e.target);
122
123 if (rootNode) {
124 e.preventDefault();
125
126 try {
21900f03
AN
127 showPostInContext(rootNode, {
128 focusOnClose: e.target,
129 });
3d58c1be
MM
130 } catch (err) {
131 showException(err);
132 }
133 }
134 });
135};