Commit 17d40eab authored by Jacob Schatz's avatar Jacob Schatz Committed by Rémy Coutable

Merge branch 'optimistic-todos' into 'master'

Adds small AJAX optimistic functionality to todos.

Fixes #13656 <br/>
A good first step and boring solution. <br/>
Will make ajax call to remove each issue.  <br/>
If issue is last in group of issues will refresh page.  <br/>
If issues remain in group will remove row with JS.  <br/>
Adds loading spinner to button and disables. <br/>

![todos-optimistic](/uploads/c6aec4a688e5125d4df55a2a3a55d4d7/todos-optimistic.gif) <br/>

cc @dzaporozhets @JobV @dbalexandre 

See merge request !2946
parent 17418461
...@@ -14,7 +14,6 @@ class Dispatcher ...@@ -14,7 +14,6 @@ class Dispatcher
path = page.split(':') path = page.split(':')
shortcut_handler = null shortcut_handler = null
switch page switch page
when 'projects:issues:index' when 'projects:issues:index'
Issues.init() Issues.init()
...@@ -25,6 +24,8 @@ class Dispatcher ...@@ -25,6 +24,8 @@ class Dispatcher
new ZenMode() new ZenMode()
when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show' when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show'
new Milestone() new Milestone()
when 'dashboard:todos:index'
new Todos()
when 'projects:milestones:new', 'projects:milestones:edit' when 'projects:milestones:new', 'projects:milestones:edit'
new ZenMode() new ZenMode()
new DropzoneInput($('.milestone-form')) new DropzoneInput($('.milestone-form'))
......
class @Todos
constructor: (@name) ->
@clearListeners()
@initBtnListeners()
clearListeners: ->
$('.done-todo').off('click')
$('.js-todos-mark-all').off('click')
initBtnListeners: ->
$('.done-todo').on('click', @doneClicked)
$('.js-todos-mark-all').on('click', @allDoneClicked)
doneClicked: (e) =>
e.preventDefault()
e.stopImmediatePropagation()
$this = $(e.currentTarget)
$this.disable()
$.ajax
type: 'POST'
url: $this.attr('href')
dataType: 'json'
data: '_method': 'delete'
success: (data) =>
@clearDone $this.closest('li')
@updateBadges data
allDoneClicked: (e) =>
e.preventDefault()
e.stopImmediatePropagation()
$this = $(e.currentTarget)
$this.disable()
$.ajax
type: 'POST'
url: $this.attr('href')
dataType: 'json'
data: '_method': 'delete'
success: (data) =>
$this.remove()
$('.js-todos-list').remove()
@updateBadges data
clearDone: ($row) ->
$ul = $row.closest('ul')
$row.remove()
if not $ul.find('li').length
$ul.parents('.panel').remove()
updateBadges: (data) ->
$('.todos-pending .badge, .todos-pending-count').text data.count
$('.todos-done .badge').text data.done_count
...@@ -208,3 +208,13 @@ ...@@ -208,3 +208,13 @@
background-color: #e4e7ed !important; background-color: #e4e7ed !important;
} }
} }
.btn-loading {
&:not(.disabled) .fa {
display: none;
}
.fa {
margin-right: 5px;
}
}
class Dashboard::TodosController < Dashboard::ApplicationController class Dashboard::TodosController < Dashboard::ApplicationController
before_action :find_todos, only: [:index, :destroy_all] before_action :find_todos, only: [:index, :destroy, :destroy_all]
def index def index
@todos = @todos.page(params[:page]).per(PER_PAGE) @todos = @todos.page(params[:page]).per(PER_PAGE)
...@@ -8,9 +8,14 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -8,9 +8,14 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def destroy def destroy
todo.done! todo.done!
todo_notice = 'Todo was successfully marked as done.'
respond_to do |format| respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' } format.html { redirect_to dashboard_todos_path, notice: todo_notice }
format.js { render nothing: true } format.js { render nothing: true }
format.json do
render json: { count: @todos.size, done_count: current_user.todos.done.count }
end
end end
end end
...@@ -20,6 +25,10 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -20,6 +25,10 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format| respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' } format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
format.js { render nothing: true } format.js { render nothing: true }
format.json do
find_todos
render json: { count: @todos.size, done_count: current_user.todos.done.count }
end
end end
end end
......
...@@ -16,7 +16,9 @@ ...@@ -16,7 +16,9 @@
- if todo.pending? - if todo.pending?
.todo-actions.pull-right .todo-actions.pull-right
= link_to 'Done', [:dashboard, todo], method: :delete, class: 'btn' = link_to [:dashboard, todo], method: :delete, class: 'btn btn-loading done-todo' do
Done
= icon('spinner spin')
.todo-body .todo-body
.todo-note .todo-note
......
...@@ -3,13 +3,15 @@ ...@@ -3,13 +3,15 @@
.top-area .top-area
%ul.nav-links %ul.nav-links
%li{class: ('active' if params[:state].blank? || params[:state] == 'pending')} - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending')
%li{class: "todos-pending #{todo_pending_active}"}
= link_to todos_filter_path(state: 'pending') do = link_to todos_filter_path(state: 'pending') do
%span %span
To do To do
%span{class: 'badge'} %span{class: 'badge'}
= todos_pending_count = todos_pending_count
%li{class: ('active' if params[:state] == 'done')} - todo_done_active = ('active' if params[:state] == 'done')
%li{class: "todos-done #{todo_done_active}"}
= link_to todos_filter_path(state: 'done') do = link_to todos_filter_path(state: 'done') do
%span %span
Done Done
...@@ -18,7 +20,9 @@ ...@@ -18,7 +20,9 @@
.nav-controls .nav-controls
- if @todos.any?(&:pending?) - if @todos.any?(&:pending?)
= link_to 'Mark all as done', destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn', method: :delete = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn btn-loading js-todos-mark-all', method: :delete do
Mark all as done
= icon('spinner spin')
.todos-filters .todos-filters
.gray-content-block.second-block .gray-content-block.second-block
...@@ -42,7 +46,7 @@ ...@@ -42,7 +46,7 @@
.prepend-top-default .prepend-top-default
- if @todos.any? - if @todos.any?
- @todos.group_by(&:project).each do |group| - @todos.group_by(&:project).each do |group|
.panel.panel-default.panel-small .panel.panel-default.panel-small.js-todos-list
- project = group[0] - project = group[0]
.panel-heading .panel-heading
= link_to project.name_with_namespace, namespace_project_path(project.namespace, project) = link_to project.name_with_namespace, namespace_project_path(project.namespace, project)
......
...@@ -41,7 +41,6 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps ...@@ -41,7 +41,6 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
click_link 'Done' click_link 'Done'
end end
expect(page).to have_content 'Todo was successfully marked as done.'
expect(page).to have_content 'To do 3' expect(page).to have_content 'To do 3'
expect(page).to have_content 'Done 1' expect(page).to have_content 'Done 1'
should_not_see_todo "John Doe assigned you merge request !#{merge_request.iid}" should_not_see_todo "John Doe assigned you merge request !#{merge_request.iid}"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment