Making WordPress.org

Changeset 2544


Ignore:
Timestamp:
02/22/2016 08:50:25 PM (9 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.