MediaWiki:Gadget-articlefeedback-core.js: Difference between revisions

From RuneRealm Wiki
Jump to navigation Jump to search
Content added Content deleted
(Created page with "→‎* <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...")
(No difference)

Revision as of 01:33, 13 October 2024

/** <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>