From f67b06ada016915211e84a7d12a063aa25e422f3 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 7 Jun 2016 09:44:01 +0100 Subject: Manually create todo for issuable Added a button into the sidebar for issues & merge requests to allow users to manually create todo items Closes #15045 --- app/assets/javascripts/right_sidebar.js.coffee | 41 ++++++++++++++++++++-- app/assets/stylesheets/pages/issuable.scss | 12 +++---- app/controllers/projects/issues_controller.rb | 14 ++++++++ .../projects/merge_requests_controller.rb | 14 ++++++++ app/finders/todos_finder.rb | 2 +- app/helpers/issuables_helper.rb | 14 ++++++++ app/models/todo.rb | 1 + app/services/todo_service.rb | 6 ++++ app/views/layouts/header/_default.html.haml | 5 ++- app/views/shared/issuable/_sidebar.html.haml | 13 ++++++- config/routes.rb | 2 ++ 11 files changed, 111 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee index c9cb0f4bb32..3ee943fe78c 100644 --- a/app/assets/javascripts/right_sidebar.js.coffee +++ b/app/assets/javascripts/right_sidebar.js.coffee @@ -43,6 +43,45 @@ class @Sidebar $('.right-sidebar') .hasClass('right-sidebar-collapsed'), { path: '/' }) + $(document) + .off 'click', '.js-issuable-todo' + .on 'click', '.js-issuable-todo', @toggleTodo + + toggleTodo: (e) -> + $this = $(@) + $btnText = $this.find('span') + data = { + todo_id: $this.attr('data-id') + } + + $.ajax( + url: $this.data('url') + type: 'POST' + dataType: 'json' + data: data + beforeSend: -> + $this.disable() + $('.js-issuable-todo-loading').removeClass 'hidden' + ).done (data) -> + $todoPendingCount = $('.todos-pending-count') + $todoPendingCount.text data.count + + $this.enable() + $('.js-issuable-todo-loading').addClass 'hidden' + + if data.count is 0 + $this.removeAttr 'data-id' + $btnText.text $this.data('todo-text') + + $todoPendingCount + .addClass 'hidden' + else + $btnText.text $this.data('mark-text') + $todoPendingCount + .removeClass 'hidden' + + if data.todo? + $this.attr 'data-id', data.todo.id sidebarDropdownLoading: (e) -> $sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon') @@ -117,5 +156,3 @@ class @Sidebar getBlock: (name) -> @sidebar.find(".block.#{name}") - - diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index ea453ce356a..acbb7e7f713 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -34,6 +34,10 @@ color: inherit; } + .issuable-header-text { + margin-top: 7px; + } + .block { @include clearfix; padding: $gl-padding 0; @@ -60,10 +64,6 @@ margin-top: 0; } - .issuable-count { - margin-top: 7px; - } - .gutter-toggle { margin-left: 20px; padding-left: 10px; @@ -250,7 +250,7 @@ } } - .issuable-pager { + .issuable-header-btn { background: $gray-normal; border: 1px solid $border-gray-normal; &:hover { @@ -263,7 +263,7 @@ } } - a:not(.issuable-pager) { + a:not(.issuable-header-btn) { &:hover { color: $md-link-color; text-decoration: none; diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 4e2d3bebb2e..5678d584d4a 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -164,6 +164,20 @@ class Projects::IssuesController < Projects::ApplicationController end end + def todo + json_data = Hash.new + + if params[:todo_id].nil? + TodoService.new.mark_todo(issue, current_user) + + json_data[:todo] = current_user.todos.find_by(state: :pending, action: Todo::MARKED, target_id: issue.id) + else + current_user.todos.find_by_id(params[:todo_id]).update(state: :done) + end + + render json: json_data.merge({ count: current_user.todos.pending.count }) + end + protected def issue diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 67e7187c10d..f0eba453caa 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -260,6 +260,20 @@ class Projects::MergeRequestsController < Projects::ApplicationController render json: response end + def todo + json_data = Hash.new + + if params[:todo_id].nil? + TodoService.new.mark_todo(merge_request, current_user) + + json_data[:todo] = current_user.todos.find_by(state: :pending, action: Todo::MARKED, target_id: merge_request.id) + else + current_user.todos.find_by_id(params[:todo_id]).update(state: :done) + end + + render json: json_data.merge({ count: current_user.todos.pending.count }) + end + protected def selected_target_project diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb index 1d88116d7d2..aa47c6c157e 100644 --- a/app/finders/todos_finder.rb +++ b/app/finders/todos_finder.rb @@ -36,7 +36,7 @@ class TodosFinder private def action_id? - action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED].include?(action_id.to_i) + action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED, Todo::MARKED].include?(action_id.to_i) end def action_id diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 40d8ce8a1d3..88ef1a6468c 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -67,6 +67,20 @@ module IssuablesHelper end end + def issuable_todo_path(issuable) + project = issuable.project + + if issuable.kind_of?(MergeRequest) + todo_namespace_project_merge_request_path(project.namespace, project, issuable.iid, :json) + else + todo_namespace_project_issue_path(project.namespace, project, issuable.iid, :json) + end + end + + def has_todo(issuable) + current_user.todos.find_by(target_id: issuable.id, state: :pending) + end + private def sidebar_gutter_collapsed? diff --git a/app/models/todo.rb b/app/models/todo.rb index 3a091373329..2792fa9b9a8 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -2,6 +2,7 @@ class Todo < ActiveRecord::Base ASSIGNED = 1 MENTIONED = 2 BUILD_FAILED = 3 + MARKED = 4 belongs_to :author, class_name: "User" belongs_to :note diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index 8e03ff8ddde..5a192e54f25 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -139,6 +139,12 @@ class TodoService pending_todos(user, attributes).update_all(state: :done) end + # When user marks an issue as todo + def mark_todo(issuable, current_user) + attributes = attributes_for_todo(issuable.project, issuable, current_user, Todo::MARKED) + create_todos(current_user, attributes) + end + private def create_todos(users, attributes) diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index ad30a367fc5..ebc9f01675a 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -27,9 +27,8 @@ %li = link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('bell fw') - - unless todos_pending_count == 0 - %span.badge.todos-pending-count - = todos_pending_count + %span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0)} + = todos_pending_count - if current_user.can_create_project? %li = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index fb906de829a..25d830b6e49 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -1,9 +1,20 @@ +- todo = has_todo(issuable) %aside.right-sidebar{ class: sidebar_gutter_collapsed_class } .issuable-sidebar - can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) .block.issuable-sidebar-header - %a.gutter-toggle.pull-right.js-sidebar-toggle{href: '#'} + %span.issuable-header-text.hide-collapsed.pull-left + Todo + %button.gutter-toggle.pull-right.js-sidebar-toggle{ type: "button", aria: { label: "Toggle sidebar" } } = sidebar_gutter_toggle_icon + %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), url: issuable_todo_path(issuable) } } + - if todo.nil? + %span + Add Todo + - else + %span + Mark Done + = icon('spin spinner', class: 'hidden js-issuable-todo-loading') = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| .block.assignee diff --git a/config/routes.rb b/config/routes.rb index 95fbe7dd9df..d018fa742cc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -679,6 +679,7 @@ Rails.application.routes.draw do post :toggle_subscription post :toggle_award_emoji post :remove_wip + post :todo end collection do @@ -759,6 +760,7 @@ Rails.application.routes.draw do get :referenced_merge_requests get :related_branches get :can_create_branch + post :todo end collection do post :bulk_update -- cgit v1.2.1 From 1e762c0609d31942c05101ca7d38fa1572ec35a2 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 7 Jun 2016 09:51:07 +0100 Subject: todo title text update for manual todos --- app/helpers/todos_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index b4923fbb138..6cfc86dfb9f 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -12,6 +12,7 @@ module TodosHelper when Todo::ASSIGNED then 'assigned you' when Todo::MENTIONED then 'mentioned you on' when Todo::BUILD_FAILED then 'The build failed for your' + when Todo::MARKED then 'todo' end end -- cgit v1.2.1 From 82be673bec39f626cc97bdaa24007684404fc25e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 7 Jun 2016 09:52:03 +0100 Subject: Fixed issue with sidebar button styling --- app/views/shared/issuable/_sidebar.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 25d830b6e49..baeee7f57ec 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -5,7 +5,7 @@ .block.issuable-sidebar-header %span.issuable-header-text.hide-collapsed.pull-left Todo - %button.gutter-toggle.pull-right.js-sidebar-toggle{ type: "button", aria: { label: "Toggle sidebar" } } + %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } = sidebar_gutter_toggle_icon %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), url: issuable_todo_path(issuable) } } - if todo.nil? -- cgit v1.2.1 From 05525b5531f570e144341faad7428a6099a82710 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 7 Jun 2016 09:55:53 +0100 Subject: Fixed issue with todo button not updating state This would happen when a todo already exists, the state of the button wouldn't update after the ajax call --- app/assets/javascripts/right_sidebar.js.coffee | 8 ++++---- app/assets/stylesheets/pages/issuable.scss | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee index 3ee943fe78c..def735d3b4a 100644 --- a/app/assets/javascripts/right_sidebar.js.coffee +++ b/app/assets/javascripts/right_sidebar.js.coffee @@ -70,18 +70,18 @@ class @Sidebar $('.js-issuable-todo-loading').addClass 'hidden' if data.count is 0 - $this.removeAttr 'data-id' - $btnText.text $this.data('todo-text') - $todoPendingCount .addClass 'hidden' else - $btnText.text $this.data('mark-text') $todoPendingCount .removeClass 'hidden' if data.todo? + $btnText.text $this.data('mark-text') $this.attr 'data-id', data.todo.id + else + $this.removeAttr 'data-id' + $btnText.text $this.data('todo-text') sidebarDropdownLoading: (e) -> $sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon') diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index acbb7e7f713..f57845ad9c9 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -263,7 +263,7 @@ } } - a:not(.issuable-header-btn) { + a { &:hover { color: $md-link-color; text-decoration: none; -- cgit v1.2.1 From a1be3241ec1f91182435a10615beac15fcfe235a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 7 Jun 2016 10:18:57 +0100 Subject: Todo tests and CHANGELOG --- CHANGELOG | 1 + spec/features/issues/todo_spec.rb | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 spec/features/issues/todo_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 3387394de5b..ae62b6b4c45 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -73,6 +73,7 @@ v 8.9.0 (unreleased) - Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav - All classes in the Banzai::ReferenceParser namespace are now instrumented - Remove deprecated issues_tracker and issues_tracker_id from project model + - Manually mark a issue or merge request as a todo v 8.8.5 (unreleased) - Ensure branch cleanup regardless of whether the GitHub import process succeeds diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb new file mode 100644 index 00000000000..b69cce3e7d7 --- /dev/null +++ b/spec/features/issues/todo_spec.rb @@ -0,0 +1,33 @@ +require 'rails_helper' + +feature 'Manually create a todo item from issue', feature: true, js: true do + let!(:project) { create(:project) } + let!(:issue) { create(:issue, project: project) } + let!(:user) { create(:user)} + + before do + project.team << [user, :master] + login_as(user) + visit namespace_project_issue_path(project.namespace, project, issue) + end + + it 'should create todo when clicking button' do + page.within '.issuable-sidebar' do + click_button 'Add Todo' + expect(page).to have_content 'Mark Done' + end + + page.within '.header-content .todos-pending-count' do + expect(page).to have_content '1' + end + end + + it 'should mark a todo as done' do + page.within '.issuable-sidebar' do + click_button 'Add Todo' + click_button 'Mark Done' + end + + expect(page).to have_selector('.todos-pending-count', visible: false) + end +end -- cgit v1.2.1 From 04c199a0ab2db012e8c5a190ce2836f22e846305 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 7 Jun 2016 10:54:02 +0100 Subject: Fixed bug with sidebar when user is not logged in --- app/helpers/issuables_helper.rb | 4 +++- app/views/shared/issuable/_sidebar.html.haml | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 88ef1a6468c..2ae7f5c5f32 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -78,7 +78,9 @@ module IssuablesHelper end def has_todo(issuable) - current_user.todos.find_by(target_id: issuable.id, state: :pending) + unless current_user.nil? + current_user.todos.find_by(target_id: issuable.id, state: :pending) + end end private diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index baeee7f57ec..e3aacb50c97 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -3,18 +3,20 @@ .issuable-sidebar - can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) .block.issuable-sidebar-header - %span.issuable-header-text.hide-collapsed.pull-left - Todo + - if current_user + %span.issuable-header-text.hide-collapsed.pull-left + Todo %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } = sidebar_gutter_toggle_icon - %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), url: issuable_todo_path(issuable) } } - - if todo.nil? - %span - Add Todo - - else - %span - Mark Done - = icon('spin spinner', class: 'hidden js-issuable-todo-loading') + - if current_user + %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), url: issuable_todo_path(issuable) } } + - if todo.nil? + %span + Add Todo + - else + %span + Mark Done + = icon('spin spinner', class: 'hidden js-issuable-todo-loading') = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| .block.assignee -- cgit v1.2.1 From f8a8999a2069dedd9ca21bde2b726a077c057576 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 7 Jun 2016 12:49:56 +0100 Subject: Cached jQuery selectors --- app/assets/javascripts/right_sidebar.js.coffee | 29 +++++++++++++------------- app/views/shared/issuable/_sidebar.html.haml | 9 ++++---- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee index def735d3b4a..ce8de7515dd 100644 --- a/app/assets/javascripts/right_sidebar.js.coffee +++ b/app/assets/javascripts/right_sidebar.js.coffee @@ -47,40 +47,41 @@ class @Sidebar .off 'click', '.js-issuable-todo' .on 'click', '.js-issuable-todo', @toggleTodo - toggleTodo: (e) -> + toggleTodo: -> $this = $(@) - $btnText = $this.find('span') - data = { - todo_id: $this.attr('data-id') - } + $todoLoading = $('.js-issuable-todo-loading') + $btnText = $('.js-issuable-todo-text', $this) $.ajax( url: $this.data('url') type: 'POST' dataType: 'json' - data: data + data: + todo_id: $this.attr('data-id') beforeSend: -> $this.disable() - $('.js-issuable-todo-loading').removeClass 'hidden' + $todoLoading.removeClass 'hidden' ).done (data) -> $todoPendingCount = $('.todos-pending-count') $todoPendingCount.text data.count $this.enable() - $('.js-issuable-todo-loading').addClass 'hidden' + $todoLoading.addClass 'hidden' if data.count is 0 - $todoPendingCount - .addClass 'hidden' + $todoPendingCount.addClass 'hidden' else - $todoPendingCount - .removeClass 'hidden' + $todoPendingCount.removeClass 'hidden' if data.todo? + $this + .attr 'aria-label', $this.data('mark-text') + .attr 'data-id', data.todo.id $btnText.text $this.data('mark-text') - $this.attr 'data-id', data.todo.id else - $this.removeAttr 'data-id' + $this + .attr 'aria-label', $this.data('todo-text') + .removeAttr 'data-id' $btnText.text $this.data('todo-text') sidebarDropdownLoading: (e) -> diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index e3aacb50c97..26052c47b0f 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -9,12 +9,11 @@ %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } = sidebar_gutter_toggle_icon - if current_user - %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), url: issuable_todo_path(issuable) } } - - if todo.nil? - %span + %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), url: issuable_todo_path(issuable) } } + %span.js-issuable-todo-text + - if todo.nil? Add Todo - - else - %span + - else Mark Done = icon('spin spinner', class: 'hidden js-issuable-todo-loading') -- cgit v1.2.1 From 20d382a891d92197620eb4e72526577a916292d7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 8 Jun 2016 09:29:19 +0100 Subject: Moved todo creation over to project todos controller --- app/assets/javascripts/right_sidebar.js.coffee | 2 ++ app/controllers/projects/issues_controller.rb | 14 ----------- .../projects/merge_requests_controller.rb | 14 ----------- app/controllers/projects/todos_controller.rb | 28 ++++++++++++++++++++++ app/helpers/issuables_helper.rb | 10 -------- app/views/shared/issuable/_sidebar.html.haml | 2 +- config/routes.rb | 4 ++-- 7 files changed, 33 insertions(+), 41 deletions(-) create mode 100644 app/controllers/projects/todos_controller.rb diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee index ce8de7515dd..18abec4f51e 100644 --- a/app/assets/javascripts/right_sidebar.js.coffee +++ b/app/assets/javascripts/right_sidebar.js.coffee @@ -58,6 +58,8 @@ class @Sidebar dataType: 'json' data: todo_id: $this.attr('data-id') + issuable_id: $this.data('issuable') + issuable_type: $this.data('issuable-type') beforeSend: -> $this.disable() $todoLoading.removeClass 'hidden' diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 5678d584d4a..4e2d3bebb2e 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -164,20 +164,6 @@ class Projects::IssuesController < Projects::ApplicationController end end - def todo - json_data = Hash.new - - if params[:todo_id].nil? - TodoService.new.mark_todo(issue, current_user) - - json_data[:todo] = current_user.todos.find_by(state: :pending, action: Todo::MARKED, target_id: issue.id) - else - current_user.todos.find_by_id(params[:todo_id]).update(state: :done) - end - - render json: json_data.merge({ count: current_user.todos.pending.count }) - end - protected def issue diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index f0eba453caa..67e7187c10d 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -260,20 +260,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController render json: response end - def todo - json_data = Hash.new - - if params[:todo_id].nil? - TodoService.new.mark_todo(merge_request, current_user) - - json_data[:todo] = current_user.todos.find_by(state: :pending, action: Todo::MARKED, target_id: merge_request.id) - else - current_user.todos.find_by_id(params[:todo_id]).update(state: :done) - end - - render json: json_data.merge({ count: current_user.todos.pending.count }) - end - protected def selected_target_project diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb new file mode 100644 index 00000000000..21745977860 --- /dev/null +++ b/app/controllers/projects/todos_controller.rb @@ -0,0 +1,28 @@ +class Projects::TodosController < Projects::ApplicationController + def create + json_data = Hash.new + + if params[:todo_id].nil? + TodoService.new.mark_todo(issuable, current_user) + + json_data[:todo] = current_user.todos.find_by(state: :pending, action: Todo::MARKED, target_id: issuable.id) + else + current_user.todos.find_by_id(params[:todo_id]).update(state: :done) + end + + render json: json_data.merge({ count: current_user.todos.pending.count }) + end + + private + + def issuable + @issuable ||= begin + case params[:issuable_type] + when "issue" + @project.issues.find(params[:issuable_id]) + when "merge_request" + @project.merge_requests.find(params[:issuable_id]) + end + end + end +end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 2ae7f5c5f32..8dbc51a689f 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -67,16 +67,6 @@ module IssuablesHelper end end - def issuable_todo_path(issuable) - project = issuable.project - - if issuable.kind_of?(MergeRequest) - todo_namespace_project_merge_request_path(project.namespace, project, issuable.iid, :json) - else - todo_namespace_project_issue_path(project.namespace, project, issuable.iid, :json) - end - end - def has_todo(issuable) unless current_user.nil? current_user.todos.find_by(target_id: issuable.id, state: :pending) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 26052c47b0f..17f623b3461 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -9,7 +9,7 @@ %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } = sidebar_gutter_toggle_icon - if current_user - %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), url: issuable_todo_path(issuable) } } + %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), issuable: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project, :json) } } %span.js-issuable-todo-text - if todo.nil? Add Todo diff --git a/config/routes.rb b/config/routes.rb index d018fa742cc..ef198a5e87a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -679,7 +679,6 @@ Rails.application.routes.draw do post :toggle_subscription post :toggle_award_emoji post :remove_wip - post :todo end collection do @@ -760,7 +759,6 @@ Rails.application.routes.draw do get :referenced_merge_requests get :related_branches get :can_create_branch - post :todo end collection do post :bulk_update @@ -791,6 +789,8 @@ Rails.application.routes.draw do end end + resources :todos, only: [:create], constraints: { id: /\d+/ } + resources :uploads, only: [:create] do collection do get ":secret/:filename", action: :show, as: :show, constraints: { filename: /[^\/]+/ } -- cgit v1.2.1 From 330e91368195e182cbfa9b41a1d5304f67d07334 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 9 Jun 2016 08:50:18 +0100 Subject: Uses update URL to update the status of a todo --- app/assets/javascripts/right_sidebar.js.coffee | 67 ++++++++++++++------------ app/controllers/projects/todos_controller.rb | 21 ++++---- app/views/shared/issuable/_sidebar.html.haml | 2 +- config/routes.rb | 2 +- 4 files changed, 51 insertions(+), 41 deletions(-) diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee index 18abec4f51e..8eb005b0a22 100644 --- a/app/assets/javascripts/right_sidebar.js.coffee +++ b/app/assets/javascripts/right_sidebar.js.coffee @@ -47,44 +47,51 @@ class @Sidebar .off 'click', '.js-issuable-todo' .on 'click', '.js-issuable-todo', @toggleTodo - toggleTodo: -> - $this = $(@) + toggleTodo: (e) => + $this = $(e.currentTarget) $todoLoading = $('.js-issuable-todo-loading') $btnText = $('.js-issuable-todo-text', $this) + ajaxType = if $this.attr('data-id') then 'PATCH' else 'POST' + ajaxUrlExtra = if $this.attr('data-id') then "/#{$this.attr('data-id')}" else '' $.ajax( - url: $this.data('url') - type: 'POST' + url: "#{$this.data('url')}#{ajaxUrlExtra}" + type: ajaxType dataType: 'json' data: - todo_id: $this.attr('data-id') issuable_id: $this.data('issuable') issuable_type: $this.data('issuable-type') - beforeSend: -> - $this.disable() - $todoLoading.removeClass 'hidden' - ).done (data) -> - $todoPendingCount = $('.todos-pending-count') - $todoPendingCount.text data.count - - $this.enable() - $todoLoading.addClass 'hidden' - - if data.count is 0 - $todoPendingCount.addClass 'hidden' - else - $todoPendingCount.removeClass 'hidden' - - if data.todo? - $this - .attr 'aria-label', $this.data('mark-text') - .attr 'data-id', data.todo.id - $btnText.text $this.data('mark-text') - else - $this - .attr 'aria-label', $this.data('todo-text') - .removeAttr 'data-id' - $btnText.text $this.data('todo-text') + beforeSend: => + @beforeTodoSend($this, $todoLoading) + ).done (data) => + @todoUpdateDone(data, $this, $btnText, $todoLoading) + + beforeTodoSend: ($btn, $todoLoading) -> + $btn.disable() + $todoLoading.removeClass 'hidden' + + todoUpdateDone: (data, $btn, $btnText, $todoLoading) -> + $todoPendingCount = $('.todos-pending-count') + $todoPendingCount.text data.count + + $btn.enable() + $todoLoading.addClass 'hidden' + + if data.count is 0 + $todoPendingCount.addClass 'hidden' + else + $todoPendingCount.removeClass 'hidden' + + if data.todo? + $btn + .attr 'aria-label', $btn.data('mark-text') + .attr 'data-id', data.todo.id + $btnText.text $btn.data('mark-text') + else + $btn + .attr 'aria-label', $btn.data('todo-text') + .removeAttr 'data-id' + $btnText.text $btn.data('todo-text') sidebarDropdownLoading: (e) -> $sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon') diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb index 21745977860..64e70a5bcc6 100644 --- a/app/controllers/projects/todos_controller.rb +++ b/app/controllers/projects/todos_controller.rb @@ -1,20 +1,23 @@ class Projects::TodosController < Projects::ApplicationController def create - json_data = Hash.new + TodoService.new.mark_todo(issuable, current_user) - if params[:todo_id].nil? - TodoService.new.mark_todo(issuable, current_user) + render json: { + todo: current_user.todos.find_by(state: :pending, action: Todo::MARKED, target_id: issuable.id), + count: current_user.todos.pending.count, + } + end - json_data[:todo] = current_user.todos.find_by(state: :pending, action: Todo::MARKED, target_id: issuable.id) - else - current_user.todos.find_by_id(params[:todo_id]).update(state: :done) - end + def update + current_user.todos.find_by_id(params[:id]).update(state: :done) - render json: json_data.merge({ count: current_user.todos.pending.count }) + render json: { + count: current_user.todos.pending.count, + } end private - + def issuable @issuable ||= begin case params[:issuable_type] diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 17f623b3461..539c4f3630a 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -9,7 +9,7 @@ %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } = sidebar_gutter_toggle_icon - if current_user - %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), issuable: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project, :json) } } + %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), issuable: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project) } } %span.js-issuable-todo-text - if todo.nil? Add Todo diff --git a/config/routes.rb b/config/routes.rb index ef198a5e87a..93dd3c938d0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -789,7 +789,7 @@ Rails.application.routes.draw do end end - resources :todos, only: [:create], constraints: { id: /\d+/ } + resources :todos, only: [:create, :update], constraints: { id: /\d+/ } resources :uploads, only: [:create] do collection do -- cgit v1.2.1 From 8abd7b35ff20214c072658a4e92e0418ae9e936a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 9 Jun 2016 08:51:40 +0100 Subject: Updated TODO description --- app/helpers/todos_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 6cfc86dfb9f..9adf5ef29f7 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -12,7 +12,7 @@ module TodosHelper when Todo::ASSIGNED then 'assigned you' when Todo::MENTIONED then 'mentioned you on' when Todo::BUILD_FAILED then 'The build failed for your' - when Todo::MARKED then 'todo' + when Todo::MARKED then 'marked this as a Todo for' end end -- cgit v1.2.1 From 16970d07e84f5967eccd928c9f9d9d7b027e91ac Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 9 Jun 2016 16:12:59 +0100 Subject: Returns created todos to control rather than re-query --- app/controllers/projects/todos_controller.rb | 4 ++-- app/services/todo_service.rb | 2 +- app/views/layouts/header/_default.html.haml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb index 64e70a5bcc6..a51bd5e2b49 100644 --- a/app/controllers/projects/todos_controller.rb +++ b/app/controllers/projects/todos_controller.rb @@ -1,9 +1,9 @@ class Projects::TodosController < Projects::ApplicationController def create - TodoService.new.mark_todo(issuable, current_user) + todos = TodoService.new.mark_todo(issuable, current_user) render json: { - todo: current_user.todos.find_by(state: :pending, action: Todo::MARKED, target_id: issuable.id), + todo: todos, count: current_user.todos.pending.count, } end diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index 5a192e54f25..e1f9ea64dc4 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -148,7 +148,7 @@ class TodoService private def create_todos(users, attributes) - Array(users).each do |user| + Array(users).map do |user| next if pending_todos(user, attributes).exists? Todo.create(attributes.merge(user_id: user.id)) end diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index ebc9f01675a..a0f560a13ec 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -27,7 +27,7 @@ %li = link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('bell fw') - %span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0)} + %span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) } = todos_pending_count - if current_user.can_create_project? %li -- cgit v1.2.1 From e737ffc48a4794d4dc8f58f20c973154eadff11b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 13 Jun 2016 14:25:58 +0100 Subject: Todo service tests --- spec/services/todo_service_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index 489c920f19f..5e46bfeebd9 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -220,6 +220,14 @@ describe TodoService, services: true do should_not_create_any_todo { service.new_note(note_on_project_snippet, john_doe) } end end + + describe '#mark_todo' do + it 'creates a todo from a issue' do + service.mark_todo(unassigned_issue, author) + + should_create_todo(user: author, target: unassigned_issue, action: Todo::MARKED) + end + end end describe 'Merge Requests' do @@ -351,6 +359,14 @@ describe TodoService, services: true do expect(second_todo.reload).not_to be_done end end + + describe '#mark_todo' do + it 'creates a todo from a merge request' do + service.mark_todo(mr_unassigned, author) + + should_create_todo(user: author, target: mr_unassigned, action: Todo::MARKED) + end + end end def should_create_todo(attributes = {}) -- cgit v1.2.1 From b22ba26caa233bc6cb56bc0b82f493713f657909 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 14 Jun 2016 08:36:34 +0100 Subject: CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ae62b6b4c45..96b27d97488 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -54,6 +54,7 @@ v 8.9.0 (unreleased) - Use Knapsack only in CI environment - Cache project build count in sidebar nav - Add milestone expire date to the right sidebar + - Manually mark a issue or merge request as a todo - Fix markdown_spec to use before instead of before(:all) to properly cleanup database after testing - Reduce number of queries needed to render issue labels in the sidebar - Improve error handling importing projects @@ -73,7 +74,6 @@ v 8.9.0 (unreleased) - Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav - All classes in the Banzai::ReferenceParser namespace are now instrumented - Remove deprecated issues_tracker and issues_tracker_id from project model - - Manually mark a issue or merge request as a todo v 8.8.5 (unreleased) - Ensure branch cleanup regardless of whether the GitHub import process succeeds -- cgit v1.2.1