3235: Make filter-by-project play nice with infinite scroll.
authorTom Clegg <tom@curoverse.com>
Sun, 27 Jul 2014 21:48:28 +0000 (17:48 -0400)
committerTom Clegg <tom@curoverse.com>
Sun, 27 Jul 2014 21:48:28 +0000 (17:48 -0400)
apps/workbench/app/assets/javascripts/infinite_scroll.js
apps/workbench/app/assets/javascripts/select_modal.js
apps/workbench/app/controllers/application_controller.rb
apps/workbench/app/controllers/collections_controller.rb
apps/workbench/app/views/application/_choose.html.erb
apps/workbench/app/views/application/_choose.js.erb
apps/workbench/app/views/application/_projects_tree_menu.html.erb
apps/workbench/app/views/layouts/application.html.erb
services/api/app/controllers/application_controller.rb

index 4df689b7ae9ed7d8688eb64a322f77d71da719b4..3e74da3581d0b95573d946e3540d5d4e1d6be4a5 100644 (file)
@@ -4,11 +4,18 @@ function maybe_load_more_content() {
     var src;                    // url for retrieving content
     var scrollHeight;
     var spinner, colspan;
+    var serial = Date.now();
     scrollHeight = scroller.scrollHeight || $('body')[0].scrollHeight;
     if ($(scroller).scrollTop() + $(scroller).height()
         >
         scrollHeight - 50) {
         container = $(this).data('infinite-container');
+        if (!$(container).attr('data-infinite-content-href0')) {
+            // Remember the first page source url, so we can refresh
+            // from page 1 later.
+            $(container).attr('data-infinite-content-href0',
+                              $(container).attr('data-infinite-content-href'));
+        }
         src = $(container).attr('data-infinite-content-href');
         if (!src)
             // Finished
@@ -29,15 +36,18 @@ function maybe_load_more_content() {
         }
         $(container).find(".spinner").detach();
         $(container).append(spinner);
+        $(container).attr('data-infinite-serial', serial);
         $.ajax(src,
                {dataType: 'json',
                 type: 'GET',
-                data: {},
-                context: {container: container, src: src}}).
-            always(function() {
-            }).
+                data: ($(container).data('infinite-content-params') || {}),
+                context: {container: container, src: src, serial: serial}}).
             fail(function(jqxhr, status, error) {
                 var $faildiv;
+                if ($(this.container).attr('data-infinite-serial') != this.serial) {
+                    // A newer request is already in progress.
+                    return;
+                }
                 if (jqxhr.readyState == 0 || jqxhr.status == 0) {
                     message = "Cancelled."
                 } else if (jqxhr.responseJSON && jqxhr.responseJSON.errors) {
@@ -54,6 +64,10 @@ function maybe_load_more_content() {
                 $(this.container).find('div.spinner').replaceWith($faildiv);
             }).
             done(function(data, status, jqxhr) {
+                if ($(this.container).attr('data-infinite-serial') != this.serial) {
+                    // A newer request is already in progress.
+                    return;
+                }
                 $(this.container).find(".spinner").detach();
                 $(this.container).append(data.content);
                 $(this.container).attr('data-infinite-content-href', data.next_page_href);
@@ -69,6 +83,18 @@ $(document).
         $retry_div.replaceWith('<div class="spinner spinner-32px spinner-h-center" />');
         $scroller.trigger('scroll');
     }).
+    on('refresh-content', '[data-infinite-scroller]', function() {
+        // Clear all rows, reset source href to initial state, and
+        // (if the container is visible) start loading content.
+        var first_page_href = $(this).attr('data-infinite-content-href0');
+        if (!first_page_href)
+            first_page_href = $(this).attr('data-infinite-content-href');
+        $(this).
+            html('').
+            attr('data-infinite-content-href', first_page_href);
+        $('.infinite-scroller').
+            trigger('scroll');
+    }).
     on('ready ajax:complete', function() {
         $('[data-infinite-scroller]').each(function() {
             var $scroller = $($(this).attr('data-infinite-scroller'));
index fe9132d5dc42f00dbbe86ebbaff0b3d8ef7dc617..07404d42b36e3c7fbe8a1c362ed3f6ab07dc907b 100644 (file)
@@ -70,6 +70,12 @@ $(document).on('click', '.selectable', function() {
             $(document).trigger(event_name!=null ? event_name : 'page-refresh',
                                 [data, status, jqxhr, this.action_data]);
         });
+}).on('click', '.chooser-show-project', function() {
+    $(this).attr('href', '#');
+    $($(this).closest('[data-filterable-target]').attr('data-filterable-target')).
+        data('infinite-content-params',
+             {'filters[]': JSON.stringify(['owner_uuid', '=', $(this).attr('data-project-uuid')])}).
+        trigger('refresh-content');
 });
 $(document).on('page-refresh', function(event, data, status, jqxhr, action_data) {
     window.location.reload();
index 9383e5cd56f57eab9cf2b51df84161df876d01ac..9eb9978a4bde8436ab316451ddf615046f1b56eb 100644 (file)
@@ -103,6 +103,16 @@ class ApplicationController < ActionController::Base
       filters = params[:filters]
       if filters.is_a? String
         filters = Oj.load filters
+      elsif filters.is_a? Array
+        filters = filters.collect do |filter|
+          if filter.is_a? String
+            # Accept filters[]=["foo","=","bar"]
+            Oj.load filter
+          else
+            # Accept filters=[["foo","=","bar"]]
+            filter
+          end
+        end
       end
       @filters += filters
     end
@@ -178,16 +188,7 @@ class ApplicationController < ActionController::Base
 
   def choose
     params[:limit] ||= 40
-    if !@objects
-      if params[:project_uuid] and !params[:project_uuid].empty?
-        # We want the chooser to show objects of the controllers's model_class
-        # type within a specific project specified by project_uuid, so fetch the
-        # project and request the contents of the project filtered on the
-        # controllers's model_class kind.
-        @objects = Group.find(params[:project_uuid]).contents({:filters => [['uuid', 'is_a', "arvados\##{ArvadosApiClient.class_kind(model_class)}"]]})
-      end
-      find_objects_for_index if !@objects
-    end
+    find_objects_for_index if !@objects
     respond_to do |f|
       if params[:partial]
         f.json {
index d0b15154a9cfd136c5c7433983cf61fda1b1fe14..5d1c49b8c5bb61fbd03ffdace1f8b3c32e539ea3 100644 (file)
@@ -48,16 +48,12 @@ class CollectionsController < ApplicationController
 
     params[:limit] ||= 40
 
-    filter = [['link_class','=','name'],
-              ['head_uuid','is_a','arvados#collection']]
-
-    if params[:project_uuid] and !params[:project_uuid].empty?
-      filter << ['tail_uuid', '=', params[:project_uuid]]
-    end
-
-    @objects = Link.filter(filter)
+    @filters += [['link_class','=','name'],
+                 ['head_uuid','is_a','arvados#collection']]
 
+    @objects = Link
     find_objects_for_index
+
     @name_links = @objects
 
     @objects = Collection.
index f292445895bf44ac36d449ea2412b2e49fb01f39..d2256ae6a7a3b5fd79464554fc05fa8b61641a42 100644 (file)
@@ -8,35 +8,37 @@
 
       <div class="modal-body">
         <div class="input-group">
+          <input type="text" class="form-control filterable-control" placeholder="Search" data-filterable-target=".modal.arv-choose .selectable-container"/>
           <% if params[:by_project] %>
-            <div class="input-group-btn">
+            <div class="input-group-btn" data-filterable-target=".modal.arv-choose .selectable-container">
               <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
-                Projects <span class="caret"></span>
+                Filter by project <span class="caret"></span>
               </button>
               <ul class="dropdown-menu" role="menu">
+                <li>
+                  <%= link_to '#', class: 'chooser-show-project' do %>
+                    Remove filter (search all projects)
+                  <% end %>
+                </li>
+                <li class="divider" />
                 <%= render partial: "projects_tree_menu", locals: {
                       :project_link_to => Proc.new do |pnode, &block|
                         link_to "#", {"class" => "chooser-show-project", "data-project-uuid" => pnode[:object].uuid }, &block
                        end,
-                      :top_button => Proc.new do %>
-                        <% link_to "#", {"class" => "chooser-show-project btn btn-xs btn-default pull-right" } do %>
-                          All <%= controller.model_class.class_for_display.pluralize.downcase %>
-                        <% end %>
-                      <% end %>
-                <% } %>
-                <li class="divider" />
-                <li><span class="navbar-text" id="chooser-breadcrumb">All <%= controller.model_class.class_for_display.pluralize.downcase %></span></li>
+                      :top_button => nil
+                    } %>
               </ul>
             </div>
           <% end %>
-          <input type="text" class="form-control filterable-control" placeholder="Search" data-filterable-target=".modal.arv-choose .selectable-container"/>
         </div>
+        <div style="height: 1em" />
         <div class="row" style="height: 20em">
          <div class="col-sm-6 container arv-filterable-list selectable-container"
               style="height: 100%; overflow-y: scroll"
               data-infinite-scroller="#choose-scroll"
               id="choose-scroll"
-              data-infinite-content-href="<%= next_page_href partial: true %>">
+              data-infinite-content-href="<%= next_page_href partial: true %>"
+              data-infinite-content-href0="<%= url_for partial: true %>">
            <%= render partial: 'choose_rows', locals: {multiple: multiple} %>
          </div>
           <div class="col-sm-6 modal-dialog-preview-pane" style="height: 100%; overflow-y: scroll">
index b033c9bf2fedf170c765b178bb510494707888ca..44c2cd93ae1e533aca002f84700d9e11beb4178c 100644 (file)
@@ -5,24 +5,3 @@ $('body > .modal-container .modal .modal-footer .btn-primary').
     attr('data-action-href', '<%= j params[:action_href] %>').
     attr('data-method', '<%= j params[:action_method] %>').
     data('action-data', <%= raw params[:action_data] %>);
-$(".chooser-show-project").on("click", function() {
-  $("#choose-scroll").html("<div class=\"spinner spinner-32px spinner-h-center\"></div>");
-  $(".modal-dialog-preview-pane").html('');
-  var t = $(this);
-  var d = {
-      partial: true,
-      multiple: <%= multiple || "false" %>
-    };
-  if (t.attr("data-project-uuid") != null) {
-    d.project_uuid = t.attr("data-project-uuid");
-  }
-  $.ajax('<%=j url_for %>', {
-    dataType: "json",
-    type: "GET",
-    data: d
-  }).done(function(data, status, jqxhr) {
-    $("#chooser-breadcrumb").text(t.text());
-    $("#choose-scroll").html(data.content);
-    $("#choose-scroll").prop("data-infinite-content-href", "next_page_href");
-  });
-});
index 876b0be65c8120e9d64a16b5d2cf03f73017c944..3a1aa9e81c4a71bedbcd58f5058502e099018503 100644 (file)
@@ -1,5 +1,5 @@
               <li role="presentation" class="dropdown-header">
-                <%= top_button.call %>
+                <%= top_button.andand.call %>
                 My projects
               </li>
               <% my_project_tree.each do |pnode| %>
index 715acf9c4a3d081c268ec121beb992a163b80adc..86a6892000431008d13a80b3e4960cf309808cd7 100644 (file)
@@ -79,6 +79,7 @@
                        action: 'choose',
                        controller: 'search',
                        title: 'Search',
+                       by_project: true,
                        action_name: 'Show',
                        action_href: url_for(controller: :actions, action: :show),
                        action_method: 'get',
index 8464a4ab5e05a515a557c5bdd9407724374642ef..58d14fd559368f896216bf123331168a9557aca0 100644 (file)
@@ -35,7 +35,7 @@ class ApplicationController < ActionController::Base
                 except: [:index, :create] + ERROR_ACTIONS)
   before_filter :load_limit_offset_order_params, only: [:index, :contents]
   before_filter :load_where_param, only: [:index, :contents]
-  before_filter :load_filters_param, only: [:index, :contents]
+  before_filter :load_filters_param, only: [:index, :contents, :choose]
   before_filter :find_objects_for_index, :only => :index
   before_filter :reload_object_before_update, :only => :update
   before_filter(:render_404_if_no_object,