MediaWiki:Gadget-articlefeedback-core.js

This is an old revision of this page, as edited by Alex (talk | contribs) at 17:14, 17 October 2024. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

After saving, you may need to bypass your browser's cache to see the changes. For further information, see Wikipedia:Bypass your cache.

  • In most Windows and Linux browsers: Hold down Ctrl and press F5.
  • In Safari: Hold down ⇧ Shift and click the Reload button.
  • In Chrome and Firefox for Mac: Hold down both ⌘ Cmd+⇧ Shift and press R.
/** <nowiki>
 * Gadget for the wiki article feedback feature.
 * @author Jayden
 */

'use strict';
(function($, mw){
    var conf = mw.config.get([
            'wgNamespaceNumber',
            'wgTitle',
            'wgAction',
            'wgArticleId',
            'wgUserGroups'
        ]),

        self = {
            // Constants
            API_ENDPOINT: 'https://api.weirdgloop.org/wiki/feedback',
            RESTRICTED_PAGES: [
            	// List of pages where only logged-in users should see the feedback button
            	'Gender', 'Makeover Mage', 'Pronouns', 'Body type'
        	],

            // Variables
            selectedRating: 0,
            submitBtn: null,
            $trigger: null,
            $feedbackInput: null,
            stack: null,
            panel1: null,
            panel2: null,
            window: null,

            /**
             * Startup method
             */
            init: function () {
            	if (self.RESTRICTED_PAGES.includes(conf.wgTitle) && !conf.wgUserGroups.includes('autoconfirmed')) {
            		return;
            	}
            	
                self.buildModal();
                self.buildTrigger();
            },

            /**
             * Build the element that triggers the modal.
             */
            buildTrigger: function () {
                var trigger = new OO.ui.ButtonWidget( {
                    label: 'Give feedback',
                    icon: 'feedback',
                } );

                trigger.on('click', function() {
                    self.openModal();
                });

                $('#firstHeading').append(
                    $('<div>').addClass('wgl-feedback-container').append(trigger.$element)
                );
            },

            /**
             * Build the modal we will show for providing article feedback.
             */
            buildModal: function () {
                var init = function (modal) {
                    self.panel1 = new OO.ui.PanelLayout( { padded: true, expanded: false } );

                    // Create star rating UI
                    var star = $("<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><path d='M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z'/></svg>").addClass('rs-feedback-star');
                    var stars = [star.clone(), star.clone(), star.clone(), star.clone(), star.clone()];

                    var i;
                    stars.map(function (star) {
                    	// When we click on a star...
                        star.on('click', function () {
                            i = stars.indexOf(star);
                            
                            var selectedLength = $('.rs-feedback-star-selected').length - 1;
                            
                            // This whole if statement could probably be 10x cleaner but I lack the brain capacity right now to try and do something nice without ES6 syntax
                            
                            if (!star.hasClass('rs-feedback-star-selected')) {
                                // ...if this DOES NOT have selected class, give this star + all previous ones it
                                for (i; i >= 0; --i) stars[i].addClass('rs-feedback-star-selected');
                            } else if (selectedLength === 0 && selectedLength === i) {
                            	// ...if this DOES have the selected class, but it's the only star, remove it
                            	$('.rs-feedback-star-selected').removeClass('rs-feedback-star-selected');
                            } else if (($('.rs-feedback-star-selected').length - 1) === i) {
                                // ...if this DOES have the selected class, and this is the last star, remove the selected class
                                for (i; i < stars.length; ++i) stars[i].removeClass('rs-feedback-star-selected');
                            } else  {
                            	// ...if this DOES have the selected class, but this is not the last star, remove everything after this
                            	stars.forEach(function (ele, ix) {
                            		if (ix > i) {
                            			ele.removeClass('rs-feedback-star-selected');
                            		}
                            	})
                            }

                            self.selectedRating = $('.rs-feedback-star-selected').length;
                        })
                    });

                    var finalStarUi = $('<div>').append(
                        $('<h5>').text('Give feedback on this page'),
                        $('<div>').addClass('rs-feedback-star-container').append(
                        	$('<div>').addClass('rs-feedback-stars').append(stars),
                        	$('<p>').addClass('text-grey small').text('(optional)')
                        )
                    );

                    self.$feedbackInput = new OO.ui.MultilineTextInputWidget( { placeholder: 'What can be improved on this page?', id: 'rs-feedback-feedback', rows: 5, autosize: true, autofocus: true} );

                    self.submitBtn = new OO.ui.ButtonInputWidget( {
                        label: 'Submit',
                        flags: [ 'primary', 'progressive' ]
                    } );
                    var b1click = ('click', function(modal) {
                        // When the submit button is clicked, do the submission...
                        self.submit();
                    });
                    self.submitBtn.on('click', b1click, [modal]);

                    var cancelBtn = new OO.ui.ButtonInputWidget( {
                        label: 'Cancel',
                    } );
                    var close = function(modal) {
                        self.closeModal();
                    };
                    cancelBtn.on('click', close, [modal]);

                    self.panel1.$element.append(finalStarUi, self.$feedbackInput.$element, $('<p>').addClass('wgl-feedback-error'), self.submitBtn.$element, cancelBtn.$element );

                    self.panel2 = new OO.ui.PanelLayout( { padded: true, expanded: false } );
                    self.panel2.$element.append(
                        $('<h3>').text('Thank you!'),
                        $('<p>').html('Your feedback has been brought up for discussion on our Discord server. You can join by clicking <strong>Open Discord App</strong>.'),
                        $('<iframe>')
                            .addClass('rs-feedback-discord')
                            .css('border-top', '1px')
                            .css('margin', '1em 0')
                            .attr('width', '100%')
                            .attr('height', '500px')
                            .attr('frameBorder', '0')
                    )

                    self.stack = new OO.ui.StackLayout({
                        items: [self.panel1, self.panel2]
                    })
                    modal.$body.append( self.stack.$element );
                };

                rs.createOOUIWindow('feedback', 'Submit feedback for ' + conf.wgTitle, {size: 'large', classes: ['rs-feedback-modal']}, init, false, true, true).then(function (w) {
                    self.window = w;
                    self.window.$element.on('click', function (e) {
                        if (e.target !== this) return;
                        self.closeModal();
                    })
                });
            },

            /**
             * Open the modal
             */
            openModal: function (e) {
                if (e) { e.preventDefault(); }
                window.OOUIWindowManager.openWindow( 'feedback' );
            },

            /**
             * Close the modal
             */
            closeModal: function () {
                self.resetForm();
                window.OOUIWindowManager.closeWindow( 'feedback' );
                self.stack.setItem(self.panel1);
            },

            /**
             * Reset the form entirely
             */
            resetForm: function () {
                self.setError('');
                self.submitBtn.setDisabled(false);
                $('.rs-feedback-star-selected').removeClass('rs-feedback-star-selected');
                self.selectedRating = 0;
            },

            /**
             * Actually do the submission
             */
            submit: function () {
                self.submitBtn.setDisabled(true);
                self.setError('');

                // Ensure that feedback was provided
                var feedbackInputValue = self.$feedbackInput.value.trim();
                if (!feedbackInputValue) {
                    self.setError('Please provide feedback!');
                    self.submitBtn.setDisabled(false);
                    return;
                };

                // Make API call to save feedback
                self.submitToAPI(self.selectedRating, feedbackInputValue);
            },

            showResultPage: function () {
              self.stack.setItem(self.panel2);
              // This is here so that we don't load the iframe until the result page is displayed
              $('.rs-feedback-discord').attr('src', 'https://e.widgetbot.io/channels/177206626514632704/269673599554551808');
              self.window.updateSize();
            },

            /**
             * Make API call
             */
            submitToAPI: function (rating, feedback) {
                $.ajax(self.API_ENDPOINT, {
                    data: JSON.stringify({wiki: 'osw', page: conf.wgArticleId, rating: rating, feedback: feedback}),
                    type: 'POST',
                    contentType: 'application/json'
                })
                    .done(function () {
                        self.showResultPage();
                    })
                    .fail(function (jqXHR) {
                        self.setError('There was a problem. Please try again later.');
                        self.submitBtn.setDisabled(false);
                    })
                ;
            },

            /**
             * Set an error message
             */
            setError: function(error) {
                $('.wgl-feedback-error').text(error);
                self.window.updateSize();
            }
        };

    mw.loader.using(['mediawiki.api', 'ext.gadget.rsw-util', 'oojs-ui-core', 'oojs-ui.styles.icons-interactions'], function () {
        $(self.init);
    });
})(window.$, window.mw);
// </nowiki>