diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | CHANGELOG | 3 | ||||
-rw-r--r-- | app/assets/javascripts/application.js.coffee | 2 | ||||
-rw-r--r-- | app/assets/javascripts/pager.js.coffee | 42 | ||||
-rw-r--r-- | app/assets/stylesheets/sections/projects.scss | 4 | ||||
-rw-r--r-- | app/controllers/application_controller.rb | 14 | ||||
-rw-r--r-- | app/controllers/projects_controller.rb | 15 | ||||
-rw-r--r-- | app/views/projects/_gitlab.html.haml | 58 | ||||
-rw-r--r-- | app/views/projects/_gl_projects.html.haml | 15 | ||||
-rw-r--r-- | app/views/projects/_search.html.haml | 7 | ||||
-rw-r--r-- | app/views/projects/gitlab.html.haml | 35 | ||||
-rw-r--r-- | app/views/projects/gitlab.js.haml | 6 | ||||
-rw-r--r-- | app/views/projects/index.html.haml | 13 | ||||
-rw-r--r-- | vendor/assets/javascripts/jquery.endless-scroll.js | 128 |
14 files changed, 269 insertions, 74 deletions
@@ -16,5 +16,4 @@ tmp/* /.bundle /db/*.sqlite3 /log/*.log -/vendor /.idea @@ -1,3 +1,6 @@ +v7.12.0 + - Endless scroll on the dashboard + v7.11.0 - Deploy Jobs API calls - Projects search on dashboard page diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 3908860..8645c78 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -15,6 +15,8 @@ #= require jquery_ujs #= require turbolinks #= require jquery.turbolinks +#= require jquery.endless-scroll +#= require pager #= require nprogress #= require nprogress-turbolinks #= require jquery_nested_form diff --git a/app/assets/javascripts/pager.js.coffee b/app/assets/javascripts/pager.js.coffee new file mode 100644 index 0000000..fd518c3 --- /dev/null +++ b/app/assets/javascripts/pager.js.coffee @@ -0,0 +1,42 @@ +@Pager = + init: (@url, @limit = 0, preload, @disable = false) -> + if preload + @offset = 0 + @getItems() + else + @offset = @limit + @initLoadMore() + + getItems: -> + $(".loading").show() + $.ajax + type: "GET" + url: @url + data: "limit=" + @limit + "&offset=" + @offset + complete: => + $(".loading").hide() + success: (data) => + Pager.append(data.count, data.html) + dataType: "json" + + append: (count, html) -> + if count > 1 + $(".content-list").append html + if count == @limit + @offset += count + else + @disable = true + + initLoadMore: -> + $(document).unbind('scroll') + $(document).endlessScroll + bottomPixels: 400 + fireDelay: 1000 + fireOnce: true + ceaseFire: -> + Pager.disable + + callback: (i) => + unless $(".loading").is(':visible') + $(".loading").show() + Pager.getItems() diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss index bc9da2f..84ee139 100644 --- a/app/assets/stylesheets/sections/projects.scss +++ b/app/assets/stylesheets/sections/projects.scss @@ -55,3 +55,7 @@ margin-bottom: 0; } } + +.loading{ + font-size: 20px; +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 316fd63..9852736 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -88,6 +88,20 @@ class ApplicationController < ActionController::Base headers['X-XSS-Protection'] = '1; mode=block' end + # JSON for infinite scroll via Pager object + def pager_json(partial, count) + html = render_to_string( + partial, + layout: false, + formats: [:html] + ) + + render json: { + html: html, + count: count + } + end + def check_config redirect_to oauth2_help_path unless valid_config? end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 7051045..4c4d34e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,5 +1,5 @@ class ProjectsController < ApplicationController - PROJECTS_PER_PAGE = 100 + PROJECTS_BATCH = 100 before_filter :authenticate_user!, except: [:build, :badge, :index, :show] before_filter :authenticate_public_page!, only: :show @@ -17,13 +17,20 @@ class ProjectsController < ApplicationController end def gitlab + @limit, @offset = (params[:limit] || PROJECTS_BATCH).to_i, (params[:offset] || 0).to_i + @page = @offset == 0 ? 1 : (@offset / @limit + 1) + current_user.reset_cache if params[:reset_cache] - @page = (params[:page] || 1).to_i - @per_page = PROJECTS_PER_PAGE - @gl_projects = current_user.gitlab_projects(params[:search], @page, @per_page) + + @gl_projects = current_user.gitlab_projects(params[:search], @page, @limit) @projects = Project.where(gitlab_id: @gl_projects.map(&:id)).ordered_by_last_commit_date @total_count = @gl_projects.size @gl_projects.reject! { |gl_project| @projects.map(&:gitlab_id).include?(gl_project.id) } + respond_to do |format| + format.json do + pager_json("projects/gitlab", @total_count) + end + end rescue Network::UnauthorizedError raise rescue diff --git a/app/views/projects/_gitlab.html.haml b/app/views/projects/_gitlab.html.haml deleted file mode 100644 index bc7ff0c..0000000 --- a/app/views/projects/_gitlab.html.haml +++ /dev/null @@ -1,58 +0,0 @@ -.clearfix.light - .pull-left.fetch-status - Fetched from GitLab (#{link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink}) - - if params[:search].present? - by keyword: "#{params[:search]}", - #{time_ago_in_words(current_user.sync_at)} ago. - = link_to gitlab_projects_path(reset_cache: true, search: params[:search]), remote: true, class: 'sync-now btn btn-small btn-default' do - %i.icon-refresh - Sync now - %br - - .pull-right - #{@total_count} projects, #{@projects.size} of them added to CI -%br - -%table.table.projects-table - %thead - %tr - %th Project Name - %th Last commit - %th Access - %th Commits - - = render @projects - - - @gl_projects.sort_by(&:name_with_namespace).each do |project| - %tr.light - %td - = project.name_with_namespace - %td - %small Not added to CI - %td - %td - - if Project.already_added?(project) - %strong.cgreen - Added - - else - = form_tag projects_path do - = hidden_field_tag :project, project.to_yaml - = submit_tag 'Add project to CI', class: 'btn btn-default btn-small' - -%ul.pagination.gitlab-projects-pagination - - if @page > 1 - %li - = link_to gitlab_projects_path(page: @page - 1), class: 'btn', remote: true do - %i.icon-angle-left - Previous - - - if @total_count == @per_page - %li - = link_to gitlab_projects_path(page: @page + 1, search: params[:search]), class: 'btn', remote: true do - Next - %i.icon-angle-right - -:coffeescript - NProgress.done() - $('.gitlab-projects-pagination').on 'ajax:before', -> - $('.projects').html('<h1><i class="icon-spinner icon-spin"/></h1>') diff --git a/app/views/projects/_gl_projects.html.haml b/app/views/projects/_gl_projects.html.haml new file mode 100644 index 0000000..15cd3f0 --- /dev/null +++ b/app/views/projects/_gl_projects.html.haml @@ -0,0 +1,15 @@ +- @gl_projects.sort_by(&:name_with_namespace).each do |project| + %tr.light + %td + = project.name_with_namespace + %td + %small Not added to CI + %td + %td + - if Project.already_added?(project) + %strong.cgreen + Added + - else + = form_tag projects_path do + = hidden_field_tag :project, project.to_yaml + = submit_tag 'Add project to CI', class: 'btn btn-default btn-small'
\ No newline at end of file diff --git a/app/views/projects/_search.html.haml b/app/views/projects/_search.html.haml index 1b350c7..a9edbb4 100644 --- a/app/views/projects/_search.html.haml +++ b/app/views/projects/_search.html.haml @@ -9,7 +9,10 @@ :coffeescript $('.search .navbar-form').submit -> - query = $('.search .navbar-form .search-input').val() - $.get '#{gitlab_projects_path}' + '?search=' + query NProgress.start() + query = $('.search .navbar-form .search-input').val() + $.get '#{gitlab_projects_path}', { search: query }, (data) -> + $(".projects").html data.html + NProgress.done() + Pager.init "#{gitlab_projects_path}" + "?search=" + query, #{ProjectsController::PROJECTS_BATCH}, false false
\ No newline at end of file diff --git a/app/views/projects/gitlab.html.haml b/app/views/projects/gitlab.html.haml new file mode 100644 index 0000000..aef0237 --- /dev/null +++ b/app/views/projects/gitlab.html.haml @@ -0,0 +1,35 @@ +- if @offset == 0 + .clearfix.light + .pull-left.fetch-status + Fetched from GitLab (#{link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink}) + - if params[:search].present? + by keyword: "#{params[:search]}", + #{time_ago_in_words(current_user.sync_at)} ago. + = link_to gitlab_projects_path(reset_cache: true, search: params[:search]), class: 'sync-now btn btn-small btn-default reset-cache' do + %i.icon-refresh + Sync now + %br + + .pull-right + #{@total_count} projects, #{@projects.size} of them added to CI + %br + + %table.table.projects-table.content-list + %thead + %tr + %th Project Name + %th Last commit + %th Access + %th Commits + + = render @projects + + = render "gl_projects" + + %p.text-center.hide.loading + %i.icon-refresh.icon-spin + +- else + = render @projects + + = render "gl_projects"
\ No newline at end of file diff --git a/app/views/projects/gitlab.js.haml b/app/views/projects/gitlab.js.haml deleted file mode 100644 index c7bdefb..0000000 --- a/app/views/projects/gitlab.js.haml +++ /dev/null @@ -1,6 +0,0 @@ -- if @error - :plain - $(".projects").html("#{@error}"); -- else - :plain - $(".projects").html("#{escape_javascript(render("gitlab"))}"); diff --git a/app/views/projects/index.html.haml b/app/views/projects/index.html.haml index 4ec8f68..45445f4 100644 --- a/app/views/projects/index.html.haml +++ b/app/views/projects/index.html.haml @@ -9,7 +9,14 @@ %p.fetch-status.light %i.icon-refresh.icon-spin Please wait while we fetch from GitLab (#{GitlabCi.config.gitlab_server.url}) - :javascript - $.get("#{gitlab_projects_path}") + :coffeescript + $.get '#{gitlab_projects_path}', (data) -> + $(".projects").html data.html + $('.projects').on 'click', '.reset-cache', -> + $.get '#{gitlab_projects_path}', { reset_cache: true }, (data) -> + $(".projects").html data.html + false + Pager.init "#{gitlab_projects_path}", #{ProjectsController::PROJECTS_BATCH}, false + - else - = render 'public' + = render 'public'
\ No newline at end of file diff --git a/vendor/assets/javascripts/jquery.endless-scroll.js b/vendor/assets/javascripts/jquery.endless-scroll.js new file mode 100644 index 0000000..38db6b0 --- /dev/null +++ b/vendor/assets/javascripts/jquery.endless-scroll.js @@ -0,0 +1,128 @@ +/** + * Endless Scroll plugin for jQuery + * + * v1.4.8 + * + * Copyright (c) 2008 Fred Wu + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + +/** + * Usage: + * + * // using default options + * $(document).endlessScroll(); + * + * // using some custom options + * $(document).endlessScroll({ + * fireOnce: false, + * fireDelay: false, + * loader: "<div class=\"loading\"><div>", + * callback: function(){ + * alert("test"); + * } + * }); + * + * Configuration options: + * + * bottomPixels integer the number of pixels from the bottom of the page that triggers the event + * fireOnce boolean only fire once until the execution of the current event is completed + * fireDelay integer delay the subsequent firing, in milliseconds, 0 or false to disable delay + * loader string the HTML to be displayed during loading + * data string|function plain HTML data, can be either a string or a function that returns a string, + * when passed as a function it accepts one argument: fire sequence (the number + * of times the event triggered during the current page session) + * insertAfter string jQuery selector syntax: where to put the loader as well as the plain HTML data + * callback function callback function, accepts one argument: fire sequence (the number of times + * the event triggered during the current page session) + * resetCounter function resets the fire sequence counter if the function returns true, this function + * could also perform hook actions since it is applied at the start of the event + * ceaseFire function stops the event (no more endless scrolling) if the function returns true + * + * Usage tips: + * + * The plugin is more useful when used with the callback function, which can then make AJAX calls to retrieve content. + * The fire sequence argument (for the callback function) is useful for 'pagination'-like features. + */ + +(function($){ + + $.fn.endlessScroll = function(options) { + + var defaults = { + bottomPixels : 50, + fireOnce : true, + fireDelay : 150, + loader : "<br />Loading...<br />", + data : "", + insertAfter : "div:last", + resetCounter : function() { return false; }, + callback : function() { return true; }, + ceaseFire : function() { return false; } + }; + + var options = $.extend({}, defaults, options), + firing = true, + fired = false, + fireSequence = 0, + is_scrollable; + + if (options.ceaseFire.apply(this) === true) + firing = false; + + if (firing === true) { + $(this).scroll(function() { + if (options.ceaseFire.apply(this) === true) { + firing = false; + return; // Scroll will still get called, but nothing will happen + } + + if (this == document || this == window) { + is_scrollable = $(document).height() - $(window).height() <= $(window).scrollTop() + options.bottomPixels; + } else { + // calculates the actual height of the scrolling container + var inner_wrap = $(".endless_scroll_inner_wrap", this); + if (inner_wrap.length == 0) + inner_wrap = $(this).wrapInner("<div class=\"endless_scroll_inner_wrap\" />").find(".endless_scroll_inner_wrap"); + is_scrollable = inner_wrap.length > 0 && + (inner_wrap.height() - $(this).height() <= $(this).scrollTop() + options.bottomPixels); + } + + if (is_scrollable && (options.fireOnce == false || (options.fireOnce == true && fired != true))) { + if (options.resetCounter.apply(this) === true) fireSequence = 0; + + fired = true; + fireSequence++; + + $(options.insertAfter).after("<div id=\"endless_scroll_loader\">" + options.loader + "</div>"); + + data = typeof options.data == 'function' ? options.data.apply(this, [fireSequence]) : options.data; + + if (data !== false) { + $(options.insertAfter).after("<div id=\"endless_scroll_data\">" + data + "</div>"); + $("#endless_scroll_data").hide().fadeIn(250, function() {$(this).removeAttr("id");}); + + options.callback.apply(this, [fireSequence]); + + if (options.fireDelay !== false || options.fireDelay !== 0) { + $("body").after("<div id=\"endless_scroll_marker\"></div>"); + // slight delay for preventing event firing twice + $("#endless_scroll_marker").fadeTo(options.fireDelay, 1, function() { + $(this).remove(); + fired = false; + }); + } + else + fired = false; + } + + $("#endless_scroll_loader").remove(); + } + }); + } + }; + +})(jQuery);
\ No newline at end of file |