Commit 04d1b412 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre 🔴
Browse files

Merge branch 'tasks' into 'master'

Add Todos

Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/2425

Tasks:

-  Prepare database
 - [X] Create a new table (`todos`)
- Tasks Queue view
 - [X] Add a number icon showing the number of todos on the top right next to the new and logout button that will redirect the user to the todos page
 - [X] Add a chronological list of todos, with the 'Todos' tab active by default
 - [X] Add a 'Done' button to each todo
 - [x] Add filters (project, author, type, and action)
- Todos generation
 - [X] When user issue/mr is assgined to someone
 - [x] When user is mentioned on (issues/mr's/comments)
- Mark todo as `done`
 - [X] When clicks on the 'Done' button
 - [X] When edit issue/mr
 - [X] When left/edit a comment
 - [X] When reassign issue/mr
 - [X] When add/remove labels to issue/mr
 - [X] When issue/mr is closed
 - [X] When mr is merged
 - [X] When added an emoji
 - [X] When changed the issue/mr milestone

* Screenshot:

![Screenshot_2016-02-20_12.45.57](/uploads/4b2554b1bde25aed3347e1ae41e8e0c0/Screenshot_2016-02-20_12.45.57.png)

See merge request !2817
parents 28f9b543 2ef1ea50
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -77,6 +77,7 @@ v 8.5.0 (unreleased)
  - Emoji comment on diffs are not award emoji
  - Emoji comment on diffs are not award emoji
  - Add label description (Nuttanart Pornprasitsakul)
  - Add label description (Nuttanart Pornprasitsakul)
  - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
  - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
  - Add Todos


v 8.4.4
v 8.4.4
  - Update omniauth-saml gem to 1.4.2
  - Update omniauth-saml gem to 1.4.2
+124 −0
Original line number Original line Diff line number Diff line
/**
 * Dashboard Todos
 *
 */

.navbar-nav {
  li {
    .badge.todos-pending-count {
      background-color: #7f8fa4;
      margin-top: -5px;
    }
  }
}

.todos {
  .panel {
    border-top: none;
    margin-bottom: 0;
  }
}

.todo-item {
  font-size: $gl-font-size;
  padding: $gl-padding-top 0 $gl-padding-top ($gl-avatar-size + $gl-padding-top);
  border-bottom: 1px solid $table-border-color;
  color: #7f8fa4;

  &.todo-inline {
    .avatar {
      position: relative;
      top: -2px;
    }

    .todo-title {
      line-height: 40px;
    }
  }

  a {
    color: #4c4e54;
  }

  .avatar {
    margin-left: -($gl-avatar-size + $gl-padding-top);
  }

  .todo-title {
    @include str-truncated(calc(100% - 174px));
    font-weight: 600;

    .author_name {
      color: #333;
    }
  }

  .todo-body {
    margin-right: 174px;

    .todo-note {
      word-wrap: break-word;

      .md {
        color: #7f8fa4;
        font-size: $gl-font-size;

        p {
          color: #5c5d5e;
        }
      }

      pre {
        border: none;
        background: #f9f9f9;
        border-radius: 0;
        color: #777;
        margin: 0 20px;
        overflow: hidden;
      }

      .note-image-attach {
        margin-top: 4px;
        margin-left: 0px;
        max-width: 200px;
        float: none;
      }

      p:last-child {
        margin-bottom: 0;
      }
    }

    .todo-note-icon {
      color: #777;
      float: left;
      font-size: $gl-font-size;
      line-height: 16px;
      margin-right: 5px;
    }
  }

  &:last-child { border:none }
}

@media (max-width: $screen-xs-max) {
  .todo-item {
    padding-left: $gl-padding;

    .todo-title {
      white-space: normal;
      overflow: visible;
      max-width: 100%;
    }

    .avatar {
      display: none;
    }

    .todo-body {
      margin: 0;
      border-left: 2px solid #DDD;
      padding-left: 10px;
    }
  }
}
+35 −0
Original line number Original line Diff line number Diff line
class Dashboard::TodosController < Dashboard::ApplicationController
  before_action :find_todos, only: [:index, :destroy_all]

  def index
    @todos = @todos.page(params[:page]).per(PER_PAGE)
  end

  def destroy
    todo.done!

    respond_to do |format|
      format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
      format.js { render nothing: true }
    end
  end

  def destroy_all
    @todos.each(&:done)

    respond_to do |format|
      format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
      format.js { render nothing: true }
    end
  end

  private

  def todo
    @todo ||= current_user.todos.find(params[:id])
  end

  def find_todos
    @todos = TodosFinder.new(current_user, params).execute
  end
end
+2 −0
Original line number Original line Diff line number Diff line
@@ -181,6 +181,8 @@ def merge
      return
      return
    end
    end


    TodoService.new.merge_merge_request(merge_request, current_user)

    @merge_request.update(merge_error: nil)
    @merge_request.update(merge_error: nil)


    if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active?
    if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active?
+129 −0
Original line number Original line Diff line number Diff line
# TodosFinder
#
# Used to filter Todos by set of params
#
# Arguments:
#   current_user - which user use
#   params:
#     action_id: integer
#     author_id: integer
#     project_id; integer
#     state: 'pending' or 'done'
#     type: 'Issue' or 'MergeRequest'
#

class TodosFinder
  NONE = '0'

  attr_accessor :current_user, :params

  def initialize(current_user, params)
    @current_user = current_user
    @params = params
  end

  def execute
    items = current_user.todos
    items = by_action_id(items)
    items = by_author(items)
    items = by_project(items)
    items = by_state(items)
    items = by_type(items)

    items
  end

  private

  def action_id?
    action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED].include?(action_id.to_i)
  end

  def action_id
    params[:action_id]
  end

  def author?
    params[:author_id].present?
  end

  def author
    return @author if defined?(@author)

    @author =
      if author? && params[:author_id] != NONE
        User.find(params[:author_id])
      else
        nil
      end
  end

  def project?
    params[:project_id].present?
  end

  def project
    return @project if defined?(@project)

    if project?
      @project = Project.find(params[:project_id])

      unless Ability.abilities.allowed?(current_user, :read_project, @project)
        @project = nil
      end
    else
      @project = nil
    end

    @project
  end

  def type?
    type.present? && ['Issue', 'MergeRequest'].include?(type)
  end

  def type
    params[:type]
  end

  def by_action_id(items)
    if action_id?
      items = items.where(action: action_id)
    end

    items
  end

  def by_author(items)
    if author?
      items = items.where(author_id: author.try(:id))
    end

    items
  end

  def by_project(items)
    if project?
      items = items.where(project: project)
    end

    items
  end

  def by_state(items)
    case params[:state]
    when 'done'
      items.done
    else
      items.pending
    end
  end

  def by_type(items)
    if type?
      items = items.where(target_type: type)
    end

    items
  end
end
Loading