Commit cff951912734a5aeea0ebb3e4ca01f704004a466
1 parent
6d5c9698
Exists in
master
and in
4 other branches
Dashboard perfomance improved. Filter for projects page
Showing
16 changed files
with
210 additions
and
107 deletions
Show diff stats
README.md
app/assets/javascripts/application.js
| ... | ... | @@ -16,7 +16,7 @@ |
| 16 | 16 | //= require branch-graph |
| 17 | 17 | //= require_tree . |
| 18 | 18 | |
| 19 | -$(function(){ | |
| 19 | +$(document).ready(function(){ | |
| 20 | 20 | $(".one_click_select").live("click", function(){ |
| 21 | 21 | $(this).select(); |
| 22 | 22 | }); |
| ... | ... | @@ -27,8 +27,50 @@ $(function(){ |
| 27 | 27 | $(".account-box").mouseenter(showMenu); |
| 28 | 28 | $(".account-box").mouseleave(resetMenu); |
| 29 | 29 | |
| 30 | + $("#projects-list .project").live('click', function(e){ | |
| 31 | + if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { | |
| 32 | + location.href = $(this).attr("url"); | |
| 33 | + e.stopPropagation(); | |
| 34 | + return false; | |
| 35 | + } | |
| 36 | + }); | |
| 37 | + | |
| 38 | + $("#issues-table .issue").live('click', function(e){ | |
| 39 | + if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { | |
| 40 | + location.href = $(this).attr("url"); | |
| 41 | + e.stopPropagation(); | |
| 42 | + return false; | |
| 43 | + } | |
| 44 | + }); | |
| 45 | + | |
| 46 | + $(document).keypress(function(e) { | |
| 47 | + if( $(e.target).is(":input") ) return; | |
| 48 | + switch(e.which) { | |
| 49 | + case 115: focusSearch(); | |
| 50 | + e.preventDefault(); | |
| 51 | + } | |
| 52 | + }); | |
| 53 | + | |
| 30 | 54 | }); |
| 31 | 55 | |
| 56 | +function focusSearch() { | |
| 57 | + $("#search").focus(); | |
| 58 | +} | |
| 59 | + | |
| 60 | +function taggifyForm(){ | |
| 61 | + var tag_field = $('#tag_field').tagify(); | |
| 62 | + | |
| 63 | + tag_field.tagify('inputField').autocomplete({ | |
| 64 | + source: '/tags.json' | |
| 65 | + }); | |
| 66 | + | |
| 67 | + $('form').submit( function() { | |
| 68 | + var tag_field = $('#tag_field') | |
| 69 | + tag_field.val( tag_field.tagify('serialize') ); | |
| 70 | + return true; | |
| 71 | + }); | |
| 72 | +} | |
| 73 | + | |
| 32 | 74 | function updatePage(data){ |
| 33 | 75 | $.ajax({type: "GET", url: location.href, data: data, dataType: "script"}); |
| 34 | 76 | } | ... | ... |
app/assets/javascripts/commits.js
| 1 | -$(document).ready(function(){ | |
| 2 | - $(".day-commits-table li.commit").live('click', function(e){ | |
| 3 | - if(e.target.nodeName != "A") { | |
| 4 | - location.href = $(this).attr("url"); | |
| 5 | - e.stopPropagation(); | |
| 6 | - return false; | |
| 7 | - } | |
| 8 | - }); | |
| 9 | -}); | |
| 10 | - | |
| 11 | 1 | var CommitsList = { |
| 2 | + ref:null, | |
| 3 | + limit:0, | |
| 4 | + offset:0, | |
| 12 | 5 | |
| 13 | -ref:null, | |
| 14 | -limit:0, | |
| 15 | -offset:0, | |
| 16 | - | |
| 17 | -init: | |
| 18 | - function(ref, limit) { | |
| 19 | - this.ref=ref; | |
| 20 | - this.limit=limit; | |
| 21 | - this.offset=limit; | |
| 22 | - this.initLoadMore(); | |
| 23 | - $('.loading').show(); | |
| 24 | - }, | |
| 25 | - | |
| 26 | -getOld: | |
| 27 | - function() { | |
| 28 | - $('.loading').show(); | |
| 29 | - $.ajax({ | |
| 30 | - type: "GET", | |
| 31 | - url: location.href, | |
| 32 | - data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref, | |
| 33 | - complete: function(){ $('.loading').hide()}, | |
| 34 | - dataType: "script"}); | |
| 35 | - }, | |
| 6 | + init: | |
| 7 | + function(ref, limit) { | |
| 8 | + $(".day-commits-table li.commit").live('click', function(e){ | |
| 9 | + if(e.target.nodeName != "A") { | |
| 10 | + location.href = $(this).attr("url"); | |
| 11 | + e.stopPropagation(); | |
| 12 | + return false; | |
| 13 | + } | |
| 14 | + }); | |
| 36 | 15 | |
| 37 | -append: | |
| 38 | - function(count, html) { | |
| 39 | - $("#commits_list").append(html); | |
| 40 | - if(count > 0) { | |
| 41 | - this.offset += count; | |
| 16 | + this.ref=ref; | |
| 17 | + this.limit=limit; | |
| 18 | + this.offset=limit; | |
| 42 | 19 | this.initLoadMore(); |
| 43 | - } | |
| 44 | - }, | |
| 20 | + $('.loading').show(); | |
| 21 | + }, | |
| 22 | + | |
| 23 | + getOld: | |
| 24 | + function() { | |
| 25 | + $('.loading').show(); | |
| 26 | + $.ajax({ | |
| 27 | + type: "GET", | |
| 28 | + url: location.href, | |
| 29 | + data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref, | |
| 30 | + complete: function(){ $('.loading').hide()}, | |
| 31 | + dataType: "script"}); | |
| 32 | + }, | |
| 45 | 33 | |
| 46 | -initLoadMore: | |
| 47 | - function() { | |
| 48 | - $(window).bind('scroll', function(){ | |
| 49 | - if($(window).scrollTop() == $(document).height() - $(window).height()){ | |
| 50 | - $(window).unbind('scroll'); | |
| 51 | - CommitsList.getOld(); | |
| 34 | + append: | |
| 35 | + function(count, html) { | |
| 36 | + $("#commits_list").append(html); | |
| 37 | + if(count > 0) { | |
| 38 | + this.offset += count; | |
| 39 | + this.initLoadMore(); | |
| 52 | 40 | } |
| 53 | - }); | |
| 54 | - } | |
| 41 | + }, | |
| 42 | + | |
| 43 | + initLoadMore: | |
| 44 | + function() { | |
| 45 | + $(window).bind('scroll', function(){ | |
| 46 | + if($(window).scrollTop() == $(document).height() - $(window).height()){ | |
| 47 | + $(window).unbind('scroll'); | |
| 48 | + CommitsList.getOld(); | |
| 49 | + } | |
| 50 | + }); | |
| 51 | + } | |
| 55 | 52 | } | ... | ... |
app/assets/javascripts/projects.js
| 1 | -$(document).ready(function(){ | |
| 2 | - $("#projects-list .project").live('click', function(e){ | |
| 3 | - if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { | |
| 4 | - location.href = $(this).attr("url"); | |
| 5 | - e.stopPropagation(); | |
| 6 | - return false; | |
| 7 | - } | |
| 8 | - }); | |
| 1 | +var ProjectsList = { | |
| 2 | + limit:0, | |
| 3 | + offset:0, | |
| 9 | 4 | |
| 10 | - $("#issues-table .issue").live('click', function(e){ | |
| 11 | - if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { | |
| 12 | - location.href = $(this).attr("url"); | |
| 13 | - e.stopPropagation(); | |
| 14 | - return false; | |
| 15 | - } | |
| 16 | - }); | |
| 5 | + init: | |
| 6 | + function(limit) { | |
| 7 | + this.limit=limit; | |
| 8 | + this.offset=limit; | |
| 9 | + this.initLoadMore(); | |
| 17 | 10 | |
| 18 | - $(document).keypress(function(e) { | |
| 19 | - if( $(e.target).is(":input") ) return; | |
| 20 | - switch(e.which) { | |
| 21 | - case 115: focusSearch(); | |
| 22 | - e.preventDefault(); | |
| 23 | - } | |
| 24 | - }); | |
| 11 | + $('.project_search').keyup(function() { | |
| 12 | + var terms = $(this).val(); | |
| 13 | + if (terms.length >= 2 || terms.length == 0) { | |
| 14 | + url = $('.project_search').parent().attr('action'); | |
| 15 | + $.ajax({ | |
| 16 | + type: "GET", | |
| 17 | + url: location.href, | |
| 18 | + data: { 'terms': terms, 'replace': true }, | |
| 19 | + dataType: "script" | |
| 20 | + }); | |
| 21 | + } | |
| 22 | + }); | |
| 23 | + }, | |
| 25 | 24 | |
| 26 | -}); | |
| 25 | + getOld: | |
| 26 | + function() { | |
| 27 | + $('.loading').show(); | |
| 28 | + $.ajax({ | |
| 29 | + type: "GET", | |
| 30 | + url: location.href, | |
| 31 | + data: "limit=" + this.limit + "&offset=" + this.offset, | |
| 32 | + complete: function(){ $('.loading').hide()}, | |
| 33 | + dataType: "script"}); | |
| 34 | + }, | |
| 27 | 35 | |
| 28 | -function focusSearch() { | |
| 29 | - $("#search").focus(); | |
| 30 | -} | |
| 36 | + replace: | |
| 37 | + function(count, html) { | |
| 38 | + $(".tile").html(html); | |
| 39 | + if(count == ProjectsList.limit) { | |
| 40 | + this.offset = count; | |
| 41 | + this.initLoadMore(); | |
| 42 | + } else { | |
| 43 | + this.offset = 0; | |
| 44 | + } | |
| 45 | + }, | |
| 31 | 46 | |
| 32 | -function taggifyForm(){ | |
| 33 | - var tag_field = $('#tag_field').tagify(); | |
| 47 | + append: | |
| 48 | + function(count, html) { | |
| 49 | + $(".tile").append(html); | |
| 50 | + if(count > 0) { | |
| 51 | + this.offset += count; | |
| 52 | + this.initLoadMore(); | |
| 53 | + } | |
| 54 | + }, | |
| 34 | 55 | |
| 35 | - tag_field.tagify('inputField').autocomplete({ | |
| 36 | - source: '/tags.json' | |
| 37 | - }); | |
| 38 | - | |
| 39 | - $('form').submit( function() { | |
| 40 | - var tag_field = $('#tag_field') | |
| 41 | - tag_field.val( tag_field.tagify('serialize') ); | |
| 42 | - return true; | |
| 43 | - }); | |
| 56 | + initLoadMore: | |
| 57 | + function() { | |
| 58 | + $(window).bind('scroll', function(){ | |
| 59 | + if($(window).scrollTop() == $(document).height() - $(window).height()){ | |
| 60 | + $(window).unbind('scroll'); | |
| 61 | + $('.loading').show(); | |
| 62 | + ProjectsList.getOld(); | |
| 63 | + } | |
| 64 | + }); | |
| 65 | + } | |
| 44 | 66 | } |
| 45 | - | ... | ... |
app/assets/stylesheets/projects.css.scss
app/controllers/dashboard_controller.rb
| ... | ... | @@ -3,7 +3,7 @@ class DashboardController < ApplicationController |
| 3 | 3 | |
| 4 | 4 | def index |
| 5 | 5 | @projects = current_user.projects.all |
| 6 | - @active_projects = @projects.select(&:last_activity_date).sort_by(&:last_activity_date).reverse | |
| 6 | + @active_projects = @projects.select(&:last_activity_date_cached).sort_by(&:last_activity_date_cached).reverse | |
| 7 | 7 | |
| 8 | 8 | respond_to do |format| |
| 9 | 9 | format.html | ... | ... |
app/controllers/projects_controller.rb
| ... | ... | @@ -11,9 +11,10 @@ class ProjectsController < ApplicationController |
| 11 | 11 | before_filter :require_non_empty_project, :only => [:blob, :tree, :graph] |
| 12 | 12 | |
| 13 | 13 | def index |
| 14 | - source = current_user.projects | |
| 15 | - source = source.tagged_with(params[:tag]) unless params[:tag].blank? | |
| 16 | - @projects = source.all | |
| 14 | + @limit, @offset = (params[:limit] || 16), (params[:offset] || 0) | |
| 15 | + @projects = current_user.projects | |
| 16 | + @projects = @projects.where("name LIKE ?", "%#{params[:terms]}%") unless params[:terms].blank? | |
| 17 | + @projects = @projects.limit(@limit).offset(@offset) | |
| 17 | 18 | end |
| 18 | 19 | |
| 19 | 20 | def new | ... | ... |
app/models/merge_request.rb
| ... | ... | @@ -35,9 +35,8 @@ class MergeRequest < ActiveRecord::Base |
| 35 | 35 | end |
| 36 | 36 | |
| 37 | 37 | def diffs |
| 38 | - commit = project.commit(source_branch) | |
| 39 | 38 | commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)} |
| 40 | - diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) | |
| 39 | + diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue [] | |
| 41 | 40 | end |
| 42 | 41 | |
| 43 | 42 | def last_commit | ... | ... |
app/models/project.rb
| ... | ... | @@ -52,6 +52,9 @@ class Project < ActiveRecord::Base |
| 52 | 52 | |
| 53 | 53 | scope :public_only, where(:private_flag => false) |
| 54 | 54 | |
| 55 | + def self.active | |
| 56 | + joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC") | |
| 57 | + end | |
| 55 | 58 | |
| 56 | 59 | def self.access_options |
| 57 | 60 | { |
| ... | ... | @@ -195,6 +198,24 @@ class Project < ActiveRecord::Base |
| 195 | 198 | last_activity.try(:created_at) |
| 196 | 199 | end |
| 197 | 200 | |
| 201 | + def last_activity_date_cached(expire = 1.hour) | |
| 202 | + activity_date_key = "project_#{id}_activity_date" | |
| 203 | + | |
| 204 | + cached_activities = Rails.cache.read(activity_date_key) | |
| 205 | + if cached_activities | |
| 206 | + activity_date = if cached_activities == "Never" | |
| 207 | + nil | |
| 208 | + else | |
| 209 | + cached_activities | |
| 210 | + end | |
| 211 | + else | |
| 212 | + activity_date = last_activity_date | |
| 213 | + Rails.cache.write(activity_date_key, activity_date || "Never", :expires_in => expire) | |
| 214 | + end | |
| 215 | + | |
| 216 | + activity_date | |
| 217 | + end | |
| 218 | + | |
| 198 | 219 | # Get project updates from cache |
| 199 | 220 | # or calculate. |
| 200 | 221 | def cached_updates(limit, expire = 2.minutes) |
| ... | ... | @@ -204,7 +225,7 @@ class Project < ActiveRecord::Base |
| 204 | 225 | activities = cached_activities |
| 205 | 226 | else |
| 206 | 227 | activities = updates(limit) |
| 207 | - Rails.cache.write(activities_key, activities, :expires_in => 60.seconds) | |
| 228 | + Rails.cache.write(activities_key, activities, :expires_in => expire) | |
| 208 | 229 | end |
| 209 | 230 | |
| 210 | 231 | activities | ... | ... |
app/views/dashboard/_sidebar.html.haml
| ... | ... | @@ -11,5 +11,5 @@ |
| 11 | 11 | %span.project-name= project.name |
| 12 | 12 | %span.time |
| 13 | 13 | %strong Last activity: |
| 14 | - = project.last_activity_date ? time_ago_in_words(project.last_activity_date) + " ago" : "Never" | |
| 14 | + = project.last_activity_date_cached ? time_ago_in_words(project.last_activity_date_cached) + " ago" : "Never" | |
| 15 | 15 | ... | ... |
app/views/merge_requests/_commits.html.haml
app/views/merge_requests/_diffs.html.haml
app/views/projects/_tile.html.haml
| ... | ... | @@ -10,10 +10,10 @@ |
| 10 | 10 | %input{ :value => project.url_to_repo, :class => ['git-url', 'one_click_select', 'text', 'project_list_url'], :readonly => 'readonly' } |
| 11 | 11 | %p.title.activity |
| 12 | 12 | %span Last Activity: |
| 13 | - - last_note = project.notes.last | |
| 14 | - = last_note ? last_note.created_at.stamp("24 Aug, 2011") : "Never" | |
| 15 | - | |
| 16 | - %p.small-tags= tag_list project | |
| 13 | + - if project.last_activity_date_cached | |
| 14 | + = project.last_activity_date_cached.stamp("24 Aug, 2011") | |
| 15 | + - else | |
| 16 | + Never | |
| 17 | 17 | |
| 18 | 18 | .buttons |
| 19 | 19 | %a.browse-code.button.yellow{:href => tree_project_ref_path(project, project.root_ref)} Browse code | ... | ... |
app/views/projects/index.html.haml
| ... | ... | @@ -7,13 +7,23 @@ |
| 7 | 7 | %h2.icon |
| 8 | 8 | %span |
| 9 | 9 | Projects |
| 10 | + %center | |
| 11 | + = form_tag projects_path, :method => :get, :remote => true, :id => "projects_search_form" do | |
| 12 | + = search_field_tag :project_search, nil, { :placeholder => 'Filter projects by name', :class => 'project_search text' } | |
| 10 | 13 | |
| 11 | 14 | %div.clear |
| 12 | 15 | - unless @projects.empty? |
| 13 | - %div{:class => "tile", :style => view_mode_style("tile")} | |
| 16 | + %div{:class => "tile"} | |
| 14 | 17 | = render "tile" |
| 15 | - %div{:class => "list", :style => view_mode_style("list")} | |
| 16 | - = render "list" | |
| 18 | + .clear | |
| 19 | + .loading{ :style => "display:none;"} | |
| 20 | + %center= image_tag "ajax-loader.gif" | |
| 21 | + | |
| 22 | + - if @projects.count == @limit | |
| 23 | + :javascript | |
| 24 | + $(function(){ | |
| 25 | + ProjectsList.init(16); | |
| 26 | + }); | |
| 17 | 27 | - else |
| 18 | 28 | %center.prepend-top |
| 19 | 29 | %h2 | ... | ... |
| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | +- if params[:replace] | |
| 2 | + :plain | |
| 3 | + ProjectsList.replace(#{@projects.count}, "#{escape_javascript(render(:partial => 'projects/tile'))}"); | |
| 4 | +- else | |
| 5 | + :plain | |
| 6 | + ProjectsList.append(#{@projects.count}, "#{escape_javascript(render(:partial => 'projects/tile'))}"); | |
| 7 | + | ... | ... |
config/initializers/rails_footnotes.rb