Making WordPress.org

Changeset 2544


Ignore:
Timestamp:
02/22/2016 08:50:25 PM (11 years ago)
Author:
ocean90
Message:

Rosetta, Roles: Improve performance of the edit page.

  • When searching filter the sub-projects collection directly and re-render the list once. Previously a CSS class was added/removed and thus every item was re-rendered. H/t adamsilverstein.
  • Only render the first 100 sub-projects of a project. The others can be revealed through a search.
  • Add an Ajax handler for the projects data and store the JSON data in browser's local storage.

See #1590.

Location:
sites/trunk/global.wordpress.org/public_html/wp-content/mu-plugins/roles
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/global.wordpress.org/public_html/wp-content/mu-plugins/roles/js/rosetta-roles.js

    r2522 r2544  
    2222
    2323                initialize: function() {
    24                         var subProjects = this.get( 'sub_projects' );
     24                        // Store the original sub-projects data, it's used to reset the collection on searches.
     25                        this._subProjects = this.get( 'sub_projects' );
    2526                        this.unset( 'sub_projects' );
    2627
    27                         this.set( 'subProjects', new projects.model.subProjects( subProjects ) );
     28                        this.set( 'subProjects', new projects.model.subProjects( this._subProjects, {
     29                                project: this,
     30                        } ) );
    2831                        this.set( 'checked', _.contains( projects.settings.accessList, parseInt( this.get( 'id' ), 10 ) ) );
    2932
     
    8487                        id: 0,
    8588                        name: '',
    86                         checked: false,
    87                         isVisible: true
     89                        checked: false
    8890                },
    8991
     
    99101                terms: '',
    100102
    101                 initialize: function() {
     103                initialize: function( models, options ) {
     104                        this.project = options.project;
    102105                        this.on( 'uncheckall', this.uncheckall );
    103106                },
     
    121124                        }
    122125
    123                         if ( this.terms === '' ) {
    124                                 this.each( function( project ) {
    125                                         project.set( 'isVisible', true );
    126                                 });
    127                         }
     126                        // If search is blank, show all projects.
     127                        if ( '' === this.terms ) {
     128                                this.reset( this.project._subProjects );
     129                        }
     130
     131                        // Trigger a 'projects:update' event
     132                        this.trigger( 'projects:update' );
    128133                },
    129134
     
    131136                // @uses RegExp
    132137                search: function( term ) {
    133                         var match;
     138                        var match, results;
     139
     140                        // Start with a full collection
     141                        this.reset( this.project._subProjects, { silent: true } );
    134142
    135143                        // Escape the term string for RegExp meta characters
     
    141149                        match = new RegExp( '^(?=.*' + term + ').+', 'i' );
    142150
    143                         // Find results
    144                         this.each( function( project ) {
     151                        results = this.filter( function( project ) {
    145152                                var haystack = _.union( [ project.get( 'name' ), project.get( 'slug' ) ] );
    146                                 project.set( 'isVisible', match.test( haystack ) );
    147                         });
     153                                return match.test( haystack );
     154                        });
     155
     156                        this.reset( results );
    148157                },
    149158
     
    172181                        });
    173182
     183                        $( '#project-loading' ).remove();
     184
    174185                        this.views.ready();
    175186
     
    263274                                collection: collection
    264275                        }) );
    265                 }
     276                },
     277
    266278        });
    267279
     
    270282                className: 'sub-projects-list',
    271283
    272                 initialize: function() {
    273                         var view = this;
    274                         this.collection.each( function( project ) {
    275                                 view.views.add( new projects.view.SubProject({
    276                                         model: project
    277                                 }) );
     284                // Number of projects which should be rendered.
     285                limit: 100,
     286
     287                initialize: function( options ) {
     288                        var self = this;
     289
     290                        this.listenTo( self.collection, 'projects:update', function() {
     291                                self.render( this );
     292                        } );
     293                },
     294
     295                render: function() {
     296                        var self = this, subProjects;
     297
     298                        self.$el.empty();
     299
     300                        subProjects = self.collection.first( self.limit );
     301                        _.each( subProjects, function( model ) {
     302                                var subProjectView = new projects.view.SubProject({
     303                                        model: model
     304                                });
     305
     306                                subProjectView.render();
     307                                self.$el.append( subProjectView.el );
    278308                        });
    279309                }
     
    287317                                model: this.model
    288318                        }) );
    289 
    290                         this.listenTo( this.model, 'change:isVisible', this.changeVisibility );
    291                 },
    292 
    293                 changeVisibility: function() {
    294                         if ( this.model.get( 'isVisible' ) ) {
    295                                 this.$el.removeClass( 'hidden' );
    296                         } else {
    297                                 this.$el.addClass( 'hidden' );
    298                         }
    299319                }
    300320        });
     
    333353                doSearch: _.debounce( function( event ) {
    334354                        this.collection.doSearch( event.target.value );
    335                 }, 200 )
    336         });
     355                }, 500 )
     356        });
     357
     358        /**
     359         * UTILS
     360         */
     361
     362        projects._hasStorage = null;
     363
     364        // Check if the browser supports localStorage.
     365        projects.hasStorage = function() {
     366                if ( null !== projects._hasStorage ) {
     367                        return projects._hasStorage;
     368                }
     369
     370                var result = false;
     371
     372                try {
     373                        window.localStorage.setItem( 'test', 'rosetta' );
     374                        result = window.localStorage.getItem( 'test' ) === 'rosetta';
     375                        window.localStorage.removeItem( 'test' );
     376                } catch( e ) {}
     377
     378                projects._hasStorage = result;
     379
     380                return projects._hasStorage;
     381        };
     382
     383        projects.getRemoteProjects = function() {
     384                return wp.ajax.post( 'rosetta-get-projects' );
     385        };
     386
     387        projects.getLocalProjects = function() {
     388                var lastUpdated = window.localStorage.getItem( 'projectsLastUpdated' );
     389                if ( ! lastUpdated ) {
     390                        return false;
     391                }
     392
     393                if ( lastUpdated < projects.settings.lastUpdated ) {
     394                        return false;
     395                }
     396
     397                var json = window.localStorage.getItem( 'projects' );
     398                if ( ! json ) {
     399                        return false;
     400                }
     401
     402                return JSON.parse( json );
     403        };
     404
     405        projects.storeLocalProjects = function( data ) {
     406                window.localStorage.setItem( 'projectsLastUpdated', projects.settings.lastUpdated );
     407                window.localStorage.setItem( 'projects', JSON.stringify( data ) );
     408        };
     409
     410        projects.initFrame = function( projectData ) {
     411                projects.view.frame = new projects.view.Frame({
     412                        collection: new projects.model.Projects( projectData )
     413                }).render();
     414        };
    337415
    338416        projects.init = function() {
    339                 projects.view.frame = new projects.view.Frame({
    340                         collection: new projects.model.Projects( projects.settings.data )
    341                 }).render();
     417                var data = null;
     418
     419                if ( projects.hasStorage() ) {
     420                        data = projects.getLocalProjects();
     421                }
     422
     423                if ( data && data.length ) {
     424                        projects.initFrame( data );
     425                        return;
     426                }
     427
     428                projects.getRemoteProjects().done( function( response ) {
     429                        projects.initFrame( response );
     430                        if ( projects.hasStorage() ) {
     431                                projects.storeLocalProjects( response );
     432                        }
     433                });
    342434        };
    343435
  • sites/trunk/global.wordpress.org/public_html/wp-content/mu-plugins/roles/rosetta-roles.php

    r2523 r2544  
    7777                add_action( 'translation_editor_added', array( $this, 'update_wporg_profile_badge' ) );
    7878                add_action( 'translation_editor_removed', array( $this, 'update_wporg_profile_badge' ) );
     79
     80                add_action( 'wp_ajax_rosetta-get-projects', array( $this, 'ajax_rosetta_get_projects' ) );
    7981        }
    8082
     
    619621         */
    620622        private function render_edit_translation_editor( $user_id ) {
     623                global $wpdb;
     624
     625                $project_access_list = $this->get_users_projects( $user_id );
     626
     627                $last_updated = $wpdb->get_var( 'SELECT meta_value FROM translate_meta WHERE object_type = "gp_option" AND meta_key = "wporg_projects_last_updated"' );
     628
     629                wp_localize_script( 'rosetta-roles', '_rosettaProjectsSettings', array(
     630                        'l10n' => array(
     631                                'searchPlaceholder' => esc_attr__( 'Search...', 'rosetta' ),
     632                        ),
     633                        'lastUpdated' => $last_updated,
     634                        'accessList' => $project_access_list,
     635                ) );
     636
     637                $feedback_message = $this->get_feedback_message();
     638
     639                require __DIR__ . '/views/edit-translation-editor.php';
     640        }
     641
     642        /**
     643         * Ajax handler for retrieving projects.
     644         */
     645        public function ajax_rosetta_get_projects() {
    621646                $projects = $this->get_translate_projects();
    622647                $project_tree = $this->get_project_tree( $projects, 0, 1 );
     
    632657                $project_tree = array_values( $project_tree );
    633658
    634                 $project_access_list = $this->get_users_projects( $user_id );
    635 
    636                 wp_localize_script( 'rosetta-roles', '_rosettaProjectsSettings', array(
    637                         'l10n' => array(
    638                                 'searchPlaceholder' => esc_attr__( 'Search...', 'rosetta' ),
    639                         ),
    640                         'data' => $project_tree,
    641                         'accessList' => $project_access_list,
    642                 ) );
    643 
    644                 $feedback_message = $this->get_feedback_message();
    645 
    646                 require __DIR__ . '/views/edit-translation-editor.php';
     659                ob_start( 'ob_gzhandler' ); // Compress JSON.
     660                wp_send_json_success( $project_tree );
    647661        }
    648662
Note: See TracChangeset for help on using the changeset viewer.