diff --git a/app/assets/javascripts/dashboard/current_tasks.js b/app/assets/javascripts/dashboard/current_tasks.js
index 1ad0380b9..1266b5390 100644
--- a/app/assets/javascripts/dashboard/current_tasks.js
+++ b/app/assets/javascripts/dashboard/current_tasks.js
@@ -1,4 +1,4 @@
-/* global dropdownSelector I18n animateSpinner PerfectSb */
+/* global dropdownSelector I18n animateSpinner PerfectSb InfiniteScroll */
/* eslint-disable no-param-reassign */
var DasboardCurrentTasksWidget = (function() {
@@ -12,7 +12,58 @@ var DasboardCurrentTasksWidget = (function() {
`;
- function loadCurrentTasksList() {
+ function generateTasksListHtml(json, container) {
+ $.each(json.data, (i, task) => {
+ var currentTaskItem;
+ var stepsPercentage = task.steps_state.percentage + '%';
+ var stateText;
+ var dueDate = (task.due_date !== null) ? ''
+ + I18n.t('dashboard.current_tasks.due_date', { date: task.due_date }) : '';
+ var overdue = (task.overdue) ? 'overdue' : '';
+ if (task.state === 'completed') {
+ stateText = I18n.t('dashboard.current_tasks.progress_bar.completed');
+ } else {
+ stateText = I18n.t('dashboard.current_tasks.progress_bar.in_progress');
+ if (task.overdue) { stateText = I18n.t('dashboard.current_tasks.progress_bar.overdue'); }
+ if (task.steps_state.all_steps !== 0) {
+ stateText += I18n.t('dashboard.current_tasks.progress_bar.completed_steps',
+ { steps: task.steps_state.completed_steps, total_steps: task.steps_state.all_steps });
+ }
+ }
+ currentTaskItem = `
+ ${task.project}/${task.experiment}
+
+
${task.name}
+
${dueDate}
+
+
+ `;
+ $(container).append(currentTaskItem);
+ });
+ }
+
+ function initInfiniteScroll() {
+ InfiniteScroll.init('.current-tasks-list', {
+ url: $('.current-tasks-list').data('tasksListUrl'),
+ customResponse: (json, container) => {
+ generateTasksListHtml(json, container);
+ },
+ customParams: (params) => {
+ params.project_id = dropdownSelector.getValues(projectFilter);
+ params.experiment_id = dropdownSelector.getValues(experimentFilter);
+ params.sort = dropdownSelector.getValues(sortFilter);
+ params.view = dropdownSelector.getValues(viewFilter);
+ params.query = $('.current-tasks-widget .task-search-field').val();
+ params.mode = $('.current-tasks-navbar .active').data('mode');
+ return params;
+ }
+ });
+ }
+
+ function loadCurrentTasksList(newList) {
var $currentTasksList = $('.current-tasks-list');
var params = {
project_id: dropdownSelector.getValues(projectFilter),
@@ -24,44 +75,16 @@ var DasboardCurrentTasksWidget = (function() {
animateSpinner($currentTasksList, true);
$.get($currentTasksList.data('tasksListUrl'), params, function(data) {
// Toggle empty state
- if (data.tasks_list.length === 0) {
+ if (data.data.length === 0) {
$currentTasksList.append(emptyState);
} else {
$currentTasksList.find('.no-tasks').remove();
}
// Clear the list
$currentTasksList.find('.current-task-item').remove();
- $.each(data.tasks_list, (i, task) => {
- var currentTaskItem;
- var stepsPercentage = task.steps_state.percentage + '%';
- var stateText;
- var dueDate = (task.due_date !== null) ? ''
- + I18n.t('dashboard.current_tasks.due_date', { date: task.due_date }) : '';
- var overdue = (task.overdue) ? 'overdue' : '';
- if (task.state === 'completed') {
- stateText = I18n.t('dashboard.current_tasks.progress_bar.completed');
- } else {
- stateText = I18n.t('dashboard.current_tasks.progress_bar.in_progress');
- if (task.overdue) { stateText = I18n.t('dashboard.current_tasks.progress_bar.overdue'); }
- if (task.steps_state.all_steps !== 0) {
- stateText += I18n.t('dashboard.current_tasks.progress_bar.completed_steps',
- { steps: task.steps_state.completed_steps, total_steps: task.steps_state.all_steps });
- }
- }
- currentTaskItem = `
- ${task.project}/${task.experiment}
-
-
${task.name}
-
${dueDate}
-
-
- `;
- $currentTasksList.append(currentTaskItem);
- });
+ generateTasksListHtml(data, $currentTasksList);
PerfectSb().update_all();
+ if (newList) InfiniteScroll.resetScroll('.current-tasks-list');
animateSpinner($currentTasksList, false);
});
}
@@ -138,7 +161,7 @@ var DasboardCurrentTasksWidget = (function() {
$('.curent-tasks-filters').dropdown('toggle');
e.stopPropagation();
e.preventDefault();
- loadCurrentTasksList();
+ loadCurrentTasksList(true);
});
}
@@ -148,7 +171,7 @@ var DasboardCurrentTasksWidget = (function() {
e.preventDefault();
$('.current-tasks-navbar').find('a').removeClass('active');
$(this).addClass('active');
- loadCurrentTasksList();
+ loadCurrentTasksList(true);
});
}
@@ -158,6 +181,7 @@ var DasboardCurrentTasksWidget = (function() {
initNavbar();
initFilters();
loadCurrentTasksList();
+ initInfiniteScroll();
}
}
};
diff --git a/app/assets/javascripts/sitewide/infinite_scroll.js b/app/assets/javascripts/sitewide/infinite_scroll.js
new file mode 100644
index 000000000..a062de0aa
--- /dev/null
+++ b/app/assets/javascripts/sitewide/infinite_scroll.js
@@ -0,0 +1,68 @@
+/* eslint-disable no-unused-vars */
+
+var InfiniteScroll = (function() {
+ function getScrollHeight($container) {
+ return $container[0].scrollHeight;
+ }
+
+ function scrollNotVisible($container) {
+ return (getScrollHeight($container) - $container.height() - 150 <= 0);
+ }
+
+ function loadData($container, page = 1) {
+ var customParams = $container.data('config').customParams;
+ var params = (customParams ? customParams({ page: page }) : { page: page });
+
+ if ($container.hasClass('loading') || $container.hasClass('last-page')) return;
+ $container.addClass('loading');
+
+ $.get($container.data('config').url, params, function(result) {
+ if ($container.data('config').customResponse) {
+ $container.data('config').customResponse(result, $container);
+ } else {
+ $(result.data).appendTo($container);
+ }
+
+ if (result.next_page) {
+ $container.data('next-page', result.next_page);
+ } else {
+ $container.addClass('last-page');
+ }
+ $container.removeClass('loading');
+
+ if (scrollNotVisible($container)) {
+ loadData($container, $container.data('next-page'));
+ }
+ });
+ }
+
+ function initScroll(object, config = {}) {
+ var $container = $(object);
+ $container.data('next-page', 2);
+ $container.data('config', config);
+
+ if (config.loadFirstPage) {
+ loadData($container, 1);
+ } else if (scrollNotVisible($container)) {
+ loadData($container, $container.data('next-page'));
+ }
+
+ $container.on('scroll', () => {
+ if ($container.scrollTop() + $container.height() > getScrollHeight($container) - 150 && !$container.hasClass('last-page')) {
+ loadData($container, $container.data('next-page'));
+ }
+ });
+ }
+
+ return {
+ init: (object, config) => {
+ initScroll(object, config);
+ },
+ resetScroll: (object) => {
+ $(object).data('next-page', 2).removeClass('last-page');
+ if (scrollNotVisible($(object))) {
+ loadData($(object), $(object).data('next-page'));
+ }
+ }
+ };
+}());
diff --git a/app/controllers/dashboard/current_tasks_controller.rb b/app/controllers/dashboard/current_tasks_controller.rb
index 340b732c4..e1da0b1d4 100644
--- a/app/controllers/dashboard/current_tasks_controller.rb
+++ b/app/controllers/dashboard/current_tasks_controller.rb
@@ -34,28 +34,25 @@ module Dashboard
tasks
end
- tasks = tasks.with_step_statistics.preload(experiment: :project)
+ page = (params[:page] || 1).to_i
+ tasks_per_page = tasks.page(page).per(Constants::INFINITE_SCROLL_LIMIT)
- respond_to do |format|
- format.json do
- render json: {
- tasks_list: tasks.map do |task|
- { id: task.id,
- link: protocols_my_module_path(task.id),
- experiment: escape_input(task.experiment.name),
- project: escape_input(task.experiment.project.name),
- name: escape_input(task.name),
- due_date: task.due_date.present? ? I18n.l(task.due_date, format: :full_with_comma) : nil,
- overdue: task.is_overdue?,
- state: task.state,
- steps_state: { completed_steps: task.steps_completed,
- all_steps: task.steps_total,
- percentage: task.steps_completed_percentage } }
- end,
- status: :ok
- }
- end
+ tasks_per_page = tasks_per_page.with_step_statistics.preload(experiment: :project)
+ tasks_list = tasks_per_page.map do |task|
+ { id: task.id,
+ link: protocols_my_module_path(task.id),
+ experiment: escape_input(task.experiment.name),
+ project: escape_input(task.experiment.project.name),
+ name: escape_input(task.name),
+ due_date: task.due_date.present? ? I18n.l(task.due_date, format: :full_with_comma) : nil,
+ overdue: task.is_overdue?,
+ state: task.state,
+ steps_state: { completed_steps: task.steps_completed,
+ all_steps: task.steps_total,
+ percentage: task.steps_completed_percentage } }
end
+
+ render json: { data: tasks_list, next_page: tasks_per_page.next_page }
end
def project_filter
@@ -83,7 +80,7 @@ module Dashboard
private
def task_filters
- params.permit(:project_id, :experiment_id, :mode, :view, :sort)
+ params.permit(:project_id, :experiment_id, :mode, :view, :sort, :page)
end
def load_project
diff --git a/app/views/dashboards/_current_tasks.html.erb b/app/views/dashboards/_current_tasks.html.erb
index 08796cdc1..ff05d50eb 100644
--- a/app/views/dashboards/_current_tasks.html.erb
+++ b/app/views/dashboards/_current_tasks.html.erb
@@ -56,7 +56,7 @@
diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb
index 7cfaed03e..78827f417 100644
--- a/config/initializers/constants.rb
+++ b/config/initializers/constants.rb
@@ -56,16 +56,14 @@ class Constants
COMMENTS_SEARCH_LIMIT = 10
# Activity limited query/display elements for pages
ACTIVITY_AND_NOTIF_SEARCH_LIMIT = 20
-
+ # Infinite Scroll load limit (elements per page)
+ INFINITE_SCROLL_LIMIT = 20
# Maximum number of users that can be invited in a single action
INVITE_USERS_LIMIT = 20
-
# Maximum nr. of search results for atwho (smart annotations)
ATWHO_SEARCH_LIMIT = 5
-
# Max characters for repository name in Atwho modal
ATWHO_REP_NAME_LIMIT = 16
-
# Number of protocols in recent protocol dropdown
RECENT_PROTOCOL_LIMIT = 14