mod/forum/amd/src/inpage_reply.js

// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * This module handles the in page replying to forum posts.
 *
 * @module     mod_forum/inpage_reply
 * @copyright  2019 Peter Dias
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
define([
    'jquery',
    'core/templates',
    'core/notification',
    'mod_forum/repository',
    'mod_forum/selectors',
    'core_form/changechecker',
], function(
    $,
    Templates,
    Notification,
    Repository,
    Selectors,
    FormChangeChecker
) {

    var DISPLAYCONSTANTS = {
        NESTED_V2: 4,
        THREADED: 2,
        NESTED: 3,
        FLAT_OLDEST_FIRST: 1,
        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}
      */
    var CONTENT_FORMATS = {
        MOODLE: 0
    };
    /**
     * Show the loading icon for the submit button.
     *
     * @param {Object} button The submit button element
     */
    var showSubmitButtonLoadingIcon = function(button) {
        var textContainer = button.find(Selectors.post.inpageSubmitBtnText);
        var loadingIconContainer = button.find(Selectors.post.loadingIconContainer);
        var width = button.outerWidth();
        // Fix the width so that the button size doesn't change when we show the loading icon.
        button.css('width', width);
        textContainer.addClass('hidden');
        loadingIconContainer.removeClass('hidden');
    };

    /**
     * Hide the loading icon for the submit button.
     *
     * @param {Object} button The submit button element
     */
    var hideSubmitButtonLoadingIcon = function(button) {
        var textContainer = button.find(Selectors.post.inpageSubmitBtnText);
        var loadingIconContainer = button.find(Selectors.post.loadingIconContainer);
        // Reset the width back to it's default.
        button.css('width', '');
        textContainer.removeClass('hidden');
        loadingIconContainer.addClass('hidden');
    };

    /**
     * Register the event listeners for the submit/cancel buttons of the in page reply.
     *
     * @param {Object} root The discussion container element.
     */
    var registerEventListeners = function(root) {
        root.on('click', Selectors.post.inpageSubmitBtn, function(e) {
            e.preventDefault();
            var submitButton = $(e.currentTarget);
            var allButtons = submitButton.parent().find(Selectors.post.inpageReplyButton);
            var form = submitButton.parents(Selectors.post.inpageReplyForm).get(0);
            var message = form.elements.post.value.trim();
            // For now, we consider the inline reply post written using the FORMAT_MOODLE (because a textarea is displayed).
            // In the future, other formats should be supported, letting users to use their preferred editor and format.
            var messageformat = CONTENT_FORMATS.MOODLE;
            // The message post will be converted from messageformat to FORMAT_HTML.
            var topreferredformat = true;
            var postid = form.elements.reply.value;
            var subject = form.elements.subject.value;
            var currentRoot = submitButton.closest(Selectors.post.post);
            var isprivatereply = form.elements.privatereply != undefined ? form.elements.privatereply.checked : false;
            var modeSelector = root.find(Selectors.post.modeSelect);
            var mode = modeSelector.length ? parseInt(modeSelector.get(0).value) : null;
            var newid;

            if (message.length) {
                showSubmitButtonLoadingIcon(submitButton);
                allButtons.prop('disabled', true);

                Repository.addDiscussionPost(postid, subject, message, messageformat, isprivatereply, topreferredformat)
                    .then(function(context) {
                        var message = context.messages.reduce(function(carry, message) {
                            if (message.type == 'success') {
                                carry += '<p>' + message.message + '</p>';
                            }
                            return carry;
                        }, '');
                        Notification.addNotification({
                            message: message,
                            type: "success"
                        });

                        return context;
                    })
                    .then(function(context) {
                        form.reset();
                        var post = context.post;
                        newid = post.id;

                        switch (mode) {
                            case DISPLAYCONSTANTS.NESTED_V2:
                                var capabilities = post.capabilities;
                                var currentAuthorName = currentRoot.children()
                                                                   .not(Selectors.post.repliesContainer)
                                                                   .find(Selectors.post.authorName)
                                                                   .text();
                                post.parentauthorname = currentAuthorName;
                                post.showactionmenu = capabilities.view ||
                                                      capabilities.controlreadstatus ||
                                                      capabilities.edit ||
                                                      capabilities.split ||
                                                      capabilities.delete ||
                                                      capabilities.export ||
                                                      post.urls.viewparent;
                                return Templates.render('mod_forum/forum_discussion_nested_v2_post_reply', post);
                            case DISPLAYCONSTANTS.THREADED:
                                return Templates.render('mod_forum/forum_discussion_threaded_post', post);
                            case DISPLAYCONSTANTS.NESTED:
                                return Templates.render('mod_forum/forum_discussion_nested_post', post);
                            default:
                                return Templates.render('mod_forum/forum_discussion_post', post);
                        }
                    })
                    .then(function(html, js) {
                        var repliesnode = currentRoot.find(Selectors.post.repliesContainer).first();

                        if (mode == DISPLAYCONSTANTS.FLAT_NEWEST_FIRST) {
                            return Templates.prependNodeContents(repliesnode, html, js);
                        } else {
                            return Templates.appendNodeContents(repliesnode, html, js);
                        }
                    })
                    .then(function() {
                        submitButton.trigger(EVENTS.POST_CREATED, newid);
                        hideSubmitButtonLoadingIcon(submitButton);
                        allButtons.prop('disabled', false);

                        // Tell formchangechecker we submitted the form.
                        FormChangeChecker.resetFormDirtyState(submitButton[0]);

                        return currentRoot.find(Selectors.post.inpageReplyContent).hide();
                    })
                    .then(function() {
                        location.href = "#p" + newid;

                        // Reload the page, say if threshold is being set by user those would get reflected through the templates.
                        location.reload();
                    })
                    .catch(function(error) {
                        hideSubmitButtonLoadingIcon(submitButton);
                        allButtons.prop('disabled', false);
                        return Notification.exception(error);
                    });
            }
        });

        root.on('click', Selectors.post.inpageCancelButton, function(e) {
            // Tell formchangechecker to reset the form state.
            FormChangeChecker.resetFormDirtyState(e.currentTarget);
        });
    };

    return {
        init: function(root) {
            registerEventListeners(root);
        },
        CONTENT_FORMATS: CONTENT_FORMATS,
        EVENTS: EVENTS
    };
});