Create table for award emoji
This commit is contained in:
		
							parent
							
								
									05920a7964
								
							
						
					
					
						commit
						3bdc57f0a7
					
				|  | @ -0,0 +1,20 @@ | |||
| module ToggleAwardEmoji | ||||
|   extend ActiveSupport::Concern | ||||
| 
 | ||||
|   included do | ||||
|     before_action :authenticate_user!, only: [:toggle_award_emoji] | ||||
|   end | ||||
| 
 | ||||
|   def toggle_award_emoji | ||||
|     name = params.require(:name) | ||||
|     awardable.toggle_award_emoji(name, current_user) | ||||
| 
 | ||||
|     render json: { ok: true } | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def awardable | ||||
|     raise NotImplementedError | ||||
|   end | ||||
| end | ||||
|  | @ -1,6 +1,7 @@ | |||
| class Projects::IssuesController < Projects::ApplicationController | ||||
|   include ToggleSubscriptionAction | ||||
|   include IssuableActions | ||||
|   include ToggleAwardEmoji | ||||
| 
 | ||||
|   before_action :module_enabled | ||||
|   before_action :issue, | ||||
|  | @ -61,7 +62,7 @@ class Projects::IssuesController < Projects::ApplicationController | |||
| 
 | ||||
|   def show | ||||
|     @note     = @project.notes.new(noteable: @issue) | ||||
|     @notes    = @issue.notes.nonawards.with_associations.fresh | ||||
|     @notes    = @issue.notes.with_associations.fresh | ||||
|     @noteable = @issue | ||||
| 
 | ||||
|     respond_to do |format| | ||||
|  | @ -158,6 +159,7 @@ class Projects::IssuesController < Projects::ApplicationController | |||
|   end | ||||
|   alias_method :subscribable_resource, :issue | ||||
|   alias_method :issuable, :issue | ||||
|   alias_method :awardable, :issue | ||||
| 
 | ||||
|   def authorize_read_issue! | ||||
|     return render_404 unless can?(current_user, :read_issue, @issue) | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController | |||
|   include ToggleSubscriptionAction | ||||
|   include DiffHelper | ||||
|   include IssuableActions | ||||
|   include ToggleAwardEmoji | ||||
| 
 | ||||
|   before_action :module_enabled | ||||
|   before_action :merge_request, only: [ | ||||
|  | @ -195,7 +196,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController | |||
| 
 | ||||
|     if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active? | ||||
|       MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) | ||||
|                                                       .execute(@merge_request) | ||||
|         .execute(@merge_request) | ||||
|       @status = :merge_when_build_succeeds | ||||
|     else | ||||
|       MergeWorker.perform_async(@merge_request.id, current_user.id, params) | ||||
|  | @ -264,6 +265,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController | |||
|   end | ||||
|   alias_method :subscribable_resource, :merge_request | ||||
|   alias_method :issuable, :merge_request | ||||
|   alias_method :awardable, :merge_request | ||||
| 
 | ||||
|   def closes_issues | ||||
|     @closes_issues ||= @merge_request.closes_issues | ||||
|  | @ -299,7 +301,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController | |||
|   def define_show_vars | ||||
|     # Build a note object for comment form | ||||
|     @note = @project.notes.new(noteable: @merge_request) | ||||
|     @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh | ||||
|     @notes = @merge_request.mr_and_commit_notes.inc_author.fresh | ||||
|     @discussions = Note.discussions_from_notes(@notes) | ||||
|     @noteable = @merge_request | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController | |||
|   before_action :authorize_read_note! | ||||
|   before_action :authorize_create_note!, only: [:create] | ||||
|   before_action :authorize_admin_note!, only: [:update, :destroy] | ||||
|   before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle] | ||||
|   before_action :find_current_user_notes, only: [:index] | ||||
| 
 | ||||
|   def index | ||||
|     current_fetched_at = Time.now.to_i | ||||
|  | @ -22,8 +22,10 @@ class Projects::NotesController < Projects::ApplicationController | |||
|   def create | ||||
|     @note = Notes::CreateService.new(project, current_user, note_params).execute | ||||
| 
 | ||||
|     @note = note.is_a?(AwardEmoji) ? @note.to_note_json : note_json(@note) | ||||
| 
 | ||||
|     respond_to do |format| | ||||
|       format.json { render json: note_json(@note) } | ||||
|       format.json { render json: @note } | ||||
|       format.html { redirect_back_or_default } | ||||
|     end | ||||
|   end | ||||
|  | @ -56,35 +58,12 @@ class Projects::NotesController < Projects::ApplicationController | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def award_toggle | ||||
|     noteable = if note_params[:noteable_type] == "issue" | ||||
|                  project.issues.find(note_params[:noteable_id]) | ||||
|                else | ||||
|                  project.merge_requests.find(note_params[:noteable_id]) | ||||
|                end | ||||
| 
 | ||||
|     data = { | ||||
|       author: current_user, | ||||
|       is_award: true, | ||||
|       note: note_params[:note].delete(":") | ||||
|     } | ||||
| 
 | ||||
|     note = noteable.notes.find_by(data) | ||||
| 
 | ||||
|     if note | ||||
|       note.destroy | ||||
|     else | ||||
|       Notes::CreateService.new(project, current_user, note_params).execute | ||||
|     end | ||||
| 
 | ||||
|     render json: { ok: true } | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def note | ||||
|     @note ||= @project.notes.find(params[:id]) | ||||
|   end | ||||
|   alias_method :awardable, :note | ||||
| 
 | ||||
|   def note_to_html(note) | ||||
|     render_to_string( | ||||
|  | @ -137,7 +116,7 @@ class Projects::NotesController < Projects::ApplicationController | |||
|         id: note.id, | ||||
|         discussion_id: note.discussion_id, | ||||
|         html: note_to_html(note), | ||||
|         award: note.is_award, | ||||
|         award: false, | ||||
|         note: note.note, | ||||
|         discussion_html: note_to_discussion_html(note), | ||||
|         discussion_with_diff_html: note_to_discussion_with_diff_html(note) | ||||
|  | @ -145,7 +124,7 @@ class Projects::NotesController < Projects::ApplicationController | |||
|     else | ||||
|       { | ||||
|         valid: false, | ||||
|         award: note.is_award, | ||||
|         award: false, | ||||
|         errors: note.errors | ||||
|       } | ||||
|     end | ||||
|  |  | |||
|  | @ -145,7 +145,7 @@ class ProjectsController < Projects::ApplicationController | |||
|     participants = ::Projects::ParticipantsService.new(@project, current_user).execute(note_type, note_id) | ||||
| 
 | ||||
|     @suggestions = { | ||||
|       emojis: AwardEmoji.urls, | ||||
|       emojis: Gitlab::AwardEmoji.urls, | ||||
|       issues: autocomplete.issues, | ||||
|       mergerequests: autocomplete.merge_requests, | ||||
|       members: participants | ||||
|  |  | |||
|  | @ -144,16 +144,17 @@ module IssuesHelper | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def emoji_author_list(notes, current_user) | ||||
|     list = notes.map do |note| | ||||
|              note.author == current_user ? "me" : note.author.name | ||||
|            end | ||||
|   def award_user_list(awards, current_user) | ||||
|     list =  | ||||
|       awards.map do |award| | ||||
|         award.user == current_user ? "me" : award.user.name | ||||
|       end | ||||
| 
 | ||||
|     list.join(", ") | ||||
|   end | ||||
| 
 | ||||
|   def note_active_class(notes, current_user) | ||||
|     if current_user && notes.pluck(:author_id).include?(current_user.id) | ||||
|   def award_active_class(awards, current_user) | ||||
|     if current_user && awards.find { |a| a.user_id == current_user.id } | ||||
|       "active" | ||||
|     else | ||||
|       "" | ||||
|  |  | |||
|  | @ -0,0 +1,35 @@ | |||
| class AwardEmoji < ActiveRecord::Base | ||||
|   DOWNVOTE_NAME = "thumbsdown".freeze | ||||
|   UPVOTE_NAME   = "thumbsup".freeze | ||||
| 
 | ||||
|   include Participable | ||||
| 
 | ||||
|   belongs_to :awardable, polymorphic: true | ||||
|   belongs_to :user | ||||
| 
 | ||||
|   validates :awardable, :user, presence: true | ||||
|   validates :name, presence: true, inclusion: { in: Emoji.emojis_names } | ||||
|   validates :name, uniqueness: { scope: [:user, :awardable_type, :awardable_id] } | ||||
| 
 | ||||
|   participant :user | ||||
| 
 | ||||
|   scope :downvotes, -> { where(name: DOWNVOTE_NAME) } | ||||
|   scope :upvotes,   -> { where(name: UPVOTE_NAME) } | ||||
| 
 | ||||
|   def downvote? | ||||
|     self.name == DOWNVOTE_NAME | ||||
|   end | ||||
| 
 | ||||
|   def upvote? | ||||
|     self.name == UPVOTE_NAME | ||||
|   end | ||||
| 
 | ||||
|   def to_note_json | ||||
|     { | ||||
|       valid:  valid?, | ||||
|       award:  true, | ||||
|       id:     id, | ||||
|       name:   name | ||||
|     } | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,81 @@ | |||
| module Awardable | ||||
|   extend ActiveSupport::Concern | ||||
| 
 | ||||
|   included do | ||||
|     has_many :award_emoji, as: :awardable, dependent: :destroy | ||||
| 
 | ||||
|     if self < Participable | ||||
|       participant :award_emoji | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   module ClassMethods | ||||
|     def order_upvotes_desc | ||||
|       order_votes_desc(AwardEmoji::UPVOTE_NAME) | ||||
|     end | ||||
| 
 | ||||
|     def order_downvotes_desc | ||||
|       order_votes_desc(AwardEmoji::DOWNVOTE_NAME) | ||||
|     end | ||||
| 
 | ||||
|     def order_votes_desc(emoji_name) | ||||
|       awardable_table = self.arel_table | ||||
|       awards_table = AwardEmoji.arel_table | ||||
| 
 | ||||
|       join_clause = awardable_table.join(awards_table, Arel::Nodes::OuterJoin).on( | ||||
|         awards_table[:awardable_id].eq(awardable_table[:id]).and( | ||||
|           awards_table[:awardable_type].eq(self.name).and( | ||||
|             awards_table[:name].eq(emoji_name) | ||||
|           ) | ||||
|         ) | ||||
|       ).join_sources | ||||
| 
 | ||||
|       joins(join_clause).group(awardable_table[:id]).reorder("COUNT(award_emoji.id) DESC") | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def grouped_awards(with_thumbs = true) | ||||
|     awards = award_emoji.group_by(&:name) | ||||
| 
 | ||||
|     if with_thumbs | ||||
|       awards[AwardEmoji::UPVOTE_NAME]   ||= AwardEmoji.none | ||||
|       awards[AwardEmoji::DOWNVOTE_NAME] ||= AwardEmoji.none | ||||
|     end | ||||
| 
 | ||||
|     awards | ||||
|   end | ||||
| 
 | ||||
|   def downvotes | ||||
|     award_emoji.where(name: AwardEmoji::DOWNVOTE_NAME).count | ||||
|   end | ||||
| 
 | ||||
|   def upvotes | ||||
|     award_emoji.where(name: AwardEmoji::UPVOTE_NAME).count | ||||
|   end | ||||
| 
 | ||||
|   def emoji_awardable? | ||||
|     true | ||||
|   end | ||||
| 
 | ||||
|   def awarded_emoji?(emoji_name, current_user) | ||||
|     award_emoji.where(name: emoji_name, user: current_user).exists? | ||||
|   end | ||||
| 
 | ||||
|   def create_award_emoji(name, current_user) | ||||
|     return unless emoji_awardable? | ||||
| 
 | ||||
|     award_emoji.create(name: name, user: current_user) | ||||
|   end | ||||
| 
 | ||||
|   def remove_award_emoji(name, current_user) | ||||
|     award_emoji.where(name: name, user: current_user).destroy_all | ||||
|   end | ||||
| 
 | ||||
|   def toggle_award_emoji(emoji_name, current_user) | ||||
|     if awarded_emoji?(emoji_name, current_user) | ||||
|       remove_award_emoji(emoji_name, current_user) | ||||
|     else | ||||
|       create_award_emoji(emoji_name, current_user) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -10,6 +10,7 @@ module Issuable | |||
|   include Mentionable | ||||
|   include Subscribable | ||||
|   include StripAttribute | ||||
|   include Awardable | ||||
| 
 | ||||
|   included do | ||||
|     belongs_to :author, class_name: "User" | ||||
|  | @ -99,29 +100,6 @@ module Issuable | |||
|         order_by(method) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def order_downvotes_desc | ||||
|       order_votes_desc('thumbsdown') | ||||
|     end | ||||
| 
 | ||||
|     def order_upvotes_desc | ||||
|       order_votes_desc('thumbsup') | ||||
|     end | ||||
| 
 | ||||
|     def order_votes_desc(award_emoji_name) | ||||
|       issuable_table = self.arel_table | ||||
|       note_table = Note.arel_table | ||||
| 
 | ||||
|       join_clause = issuable_table.join(note_table, Arel::Nodes::OuterJoin).on( | ||||
|         note_table[:noteable_id].eq(issuable_table[:id]).and( | ||||
|           note_table[:noteable_type].eq(self.name).and( | ||||
|             note_table[:is_award].eq(true).and(note_table[:note].eq(award_emoji_name)) | ||||
|           ) | ||||
|         ) | ||||
|       ).join_sources | ||||
| 
 | ||||
|       joins(join_clause).group(issuable_table[:id]).reorder("COUNT(notes.id) DESC") | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def today? | ||||
|  | @ -144,14 +122,6 @@ module Issuable | |||
|     opened? || reopened? | ||||
|   end | ||||
| 
 | ||||
|   def downvotes | ||||
|     notes.awards.where(note: "thumbsdown").count | ||||
|   end | ||||
| 
 | ||||
|   def upvotes | ||||
|     notes.awards.where(note: "thumbsup").count | ||||
|   end | ||||
| 
 | ||||
|   def subscribed_without_subscriptions?(user) | ||||
|     participants(user).include?(user) | ||||
|   end | ||||
|  |  | |||
|  | @ -36,6 +36,7 @@ class MergeRequest < ActiveRecord::Base | |||
|   include Referable | ||||
|   include Sortable | ||||
|   include Taskable | ||||
|   include Awardable | ||||
| 
 | ||||
|   belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project" | ||||
|   belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project" | ||||
|  |  | |||
|  | @ -16,7 +16,6 @@ | |||
| #  system        :boolean          default(FALSE), not null | ||||
| #  st_diff       :text | ||||
| #  updated_by_id :integer | ||||
| #  is_award      :boolean          default(FALSE), not null | ||||
| # | ||||
| 
 | ||||
| require 'carrierwave/orm/activerecord' | ||||
|  | @ -43,12 +42,9 @@ class Note < ActiveRecord::Base | |||
|   delegate :name, to: :project, prefix: true | ||||
|   delegate :name, :email, to: :author, prefix: true | ||||
| 
 | ||||
|   before_validation :set_award! | ||||
|   before_validation :clear_blank_line_code! | ||||
| 
 | ||||
|   validates :note, :project, presence: true | ||||
|   validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award } | ||||
|   validates :note, inclusion: { in: Emoji.emojis_names }, if: ->(n) { n.is_award } | ||||
|   validates :line_code, line_code: true, allow_blank: true | ||||
|   # Attachments are deprecated and are handled by Markdown uploader | ||||
|   validates :attachment, file_size: { maximum: :max_attachment_size } | ||||
|  | @ -60,8 +56,6 @@ class Note < ActiveRecord::Base | |||
|   mount_uploader :attachment, AttachmentUploader | ||||
| 
 | ||||
|   # Scopes | ||||
|   scope :awards, ->{ where(is_award: true) } | ||||
|   scope :nonawards, ->{ where(is_award: false) } | ||||
|   scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } | ||||
|   scope :inline, ->{ where("line_code IS NOT NULL") } | ||||
|   scope :not_inline, ->{ where(line_code: nil) } | ||||
|  | @ -119,19 +113,6 @@ class Note < ActiveRecord::Base | |||
| 
 | ||||
|       where(table[:note].matches(pattern)) | ||||
|     end | ||||
| 
 | ||||
|     def grouped_awards | ||||
|       notes = {} | ||||
| 
 | ||||
|       awards.select(:note).distinct.map do |note| | ||||
|         notes[note.note] = where(note: note.note) | ||||
|       end | ||||
| 
 | ||||
|       notes["thumbsup"] ||= Note.none | ||||
|       notes["thumbsdown"] ||= Note.none | ||||
| 
 | ||||
|       notes | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def cross_reference? | ||||
|  | @ -347,37 +328,25 @@ class Note < ActiveRecord::Base | |||
|     Event.reset_event_cache_for(self) | ||||
|   end | ||||
| 
 | ||||
|   def downvote? | ||||
|     is_award && note == "thumbsdown" | ||||
|   end | ||||
| 
 | ||||
|   def upvote? | ||||
|     is_award && note == "thumbsup" | ||||
|   def system? | ||||
|     read_attribute(:system) | ||||
|   end | ||||
| 
 | ||||
|   def editable? | ||||
|     !system? && !is_award | ||||
|     !system? | ||||
|   end | ||||
| 
 | ||||
|   def cross_reference_not_visible_for?(user) | ||||
|     cross_reference? && referenced_mentionables(user).empty? | ||||
|   end | ||||
| 
 | ||||
|   # Checks if note is an award added as a comment | ||||
|   # | ||||
|   # If note is an award, this method sets is_award to true | ||||
|   #   and changes content of the note to award name. | ||||
|   # | ||||
|   # Method is executed as a before_validation callback. | ||||
|   # | ||||
|   def set_award! | ||||
|     return unless awards_supported? && contains_emoji_only? | ||||
| 
 | ||||
|     self.is_award = true | ||||
|     self.note = award_emoji_name | ||||
|   def award_emoji? | ||||
|     award_emoji_supported? && contains_emoji_only? | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
|   def create_award_emoji | ||||
|     self.noteable.award_emoji(award_emoji_name, author)	 | ||||
|   end | ||||
| 
 | ||||
|   def clear_blank_line_code! | ||||
|     self.line_code = nil if self.line_code.blank? | ||||
|  | @ -389,8 +358,8 @@ class Note < ActiveRecord::Base | |||
|     diffs.find { |d| d.new_path == self.diff.new_path } | ||||
|   end | ||||
| 
 | ||||
|   def awards_supported? | ||||
|     (for_issue? || for_merge_request?) && !for_diff_line? | ||||
|   def award_emoji_supported? | ||||
|     noteable.is_a?(Awardable) && !for_diff_line? | ||||
|   end | ||||
| 
 | ||||
|   def contains_emoji_only? | ||||
|  | @ -399,6 +368,6 @@ class Note < ActiveRecord::Base | |||
| 
 | ||||
|   def award_emoji_name | ||||
|     original_name = note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1] | ||||
|     AwardEmoji.normilize_emoji_name(original_name) | ||||
|     Gitlab::AwardEmoji.normilize_emoji_name(original_name) | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -144,6 +144,7 @@ class User < ActiveRecord::Base | |||
|   has_many :builds,                   dependent: :nullify, class_name: 'Ci::Build' | ||||
|   has_many :todos,                    dependent: :destroy | ||||
|   has_many :notification_settings,    dependent: :destroy | ||||
|   has_many :award_emoji, 							as: :awardable, dependent: :destroy | ||||
| 
 | ||||
|   # | ||||
|   # Validations | ||||
|  |  | |||
|  | @ -5,6 +5,11 @@ module Notes | |||
|       note.author = current_user | ||||
|       note.system = false | ||||
| 
 | ||||
|       if note.award_emoji? | ||||
|         return ToggleAwardEmojiService.new(project, current_user, params). | ||||
|                                         execute(note.noteable, note.note) | ||||
|       end | ||||
| 
 | ||||
|       if note.save | ||||
|         # Finish the harder work in the background | ||||
|         NewNoteWorker.perform_in(2.seconds, note.id, params) | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ module Notes | |||
| 
 | ||||
|     def execute | ||||
|       # Skip system notes, like status changes and cross-references and awards | ||||
|       unless @note.system || @note.is_award | ||||
|       unless @note.system | ||||
|         EventCreateService.new.leave_note(@note, @note.author) | ||||
|         @note.create_cross_references! | ||||
|         execute_note_hooks | ||||
|  |  | |||
|  | @ -131,7 +131,6 @@ class NotificationService | |||
|     # ignore gitlab service messages | ||||
|     return true if note.note.start_with?('Status changed to closed') | ||||
|     return true if note.cross_reference? && note.system == true | ||||
|     return true if note.is_award | ||||
| 
 | ||||
|     target = note.noteable | ||||
| 
 | ||||
|  |  | |||
|  | @ -98,6 +98,14 @@ class TodoService | |||
|     handle_note(note, current_user) | ||||
|   end | ||||
| 
 | ||||
|   # When an emoji is awarded we should: | ||||
|   # | ||||
|   #  * mark all pending todos related to the awardable for the current user as done | ||||
|   # | ||||
|   def new_award_emoji(awardable, current_user) | ||||
|     mark_pending_todos_as_done(awardable, current_user) | ||||
|   end | ||||
| 
 | ||||
|   # When marking pending todos as done we should: | ||||
|   # | ||||
|   #  * mark all pending todos related to the target for the current user as done | ||||
|  |  | |||
|  | @ -0,0 +1,21 @@ | |||
| require_relative 'base_service' | ||||
| 
 | ||||
| class ToggleAwardEmojiService < BaseService | ||||
|   # For an award emoji being posted we should: | ||||
|   # - Mark the TODO as done for this issuable (skip on snippets) | ||||
|   # - Save the award emoji | ||||
|   def execute(awardable, emoji) | ||||
|     todo_service.new_award_emoji(awardable, current_user) | ||||
| 
 | ||||
|     # Needed if its posted as a note containing only :+1: | ||||
|     emoji = award_emoji_name(emoji) if emoji.start_with? ':' | ||||
|     awardable.toggle_award_emoji(emoji, current_user) | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def award_emoji_name(emoji) | ||||
|     original_name = emoji.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1] | ||||
|     Gitlab::AwardEmoji.normalize_emoji_name(original_name) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,15 @@ | |||
| - grouped_awards = awardable.grouped_awards(inline) | ||||
| .awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.size == 0), data: { award_url: url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable]) } } | ||||
|   - awards_sort(grouped_awards).each do |emoji, awards| | ||||
|     %button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button", class: (award_active_class(awards, current_user)), title: award_user_list(awards, current_user), data: { placement: "bottom" } } | ||||
|       = emoji_icon(emoji) | ||||
|       %span.award-control-text.js-counter | ||||
|         = awards.count | ||||
| 
 | ||||
|   - if current_user | ||||
|     .award-menu-holder.js-award-holder | ||||
|       %button.btn.award-control.js-add-award{ type: "button", data: { award_menu_url: emojis_path } } | ||||
|         = icon('smile-o', {class: "award-control-icon award-control-icon-normal"}) | ||||
|         = icon('spinner spin', {class: "award-control-icon award-control-icon-loading"}) | ||||
|         %span.award-control-text | ||||
|           Add | ||||
|  | @ -1,9 +1,9 @@ | |||
| .emoji-menu | ||||
|   .emoji-menu-content | ||||
|     = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control" | ||||
|     - AwardEmoji.emoji_by_category.each do |category, emojis| | ||||
|     - Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis| | ||||
|       %h5.emoji-menu-title | ||||
|         = AwardEmoji::CATEGORIES[category] | ||||
|         = Gitlab::AwardEmoji::CATEGORIES[category] | ||||
|       %ul.clearfix.emoji-menu-list | ||||
|         - emojis.each do |emoji| | ||||
|           %li.pull-left.text-center.emoji-menu-list-item | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ | |||
|           = icon('thumbs-down') | ||||
|           = downvotes | ||||
| 
 | ||||
|       - note_count = issue.notes.user.nonawards.count | ||||
|       - note_count = issue.notes.user.count | ||||
|       - if note_count > 0 | ||||
|         %li | ||||
|           = link_to issue_path(issue) + "#notes" do | ||||
|  |  | |||
|  | @ -72,7 +72,7 @@ | |||
| 
 | ||||
|     .content-block.content-block-small | ||||
|       = render 'new_branch' | ||||
|       = render 'votes/votes_block', votable: @issue | ||||
|       = render 'award_emoji/awards_block', awardable: @issue, inline: true | ||||
| 
 | ||||
|     .row | ||||
|       %section.col-md-12 | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ | |||
|           = icon('thumbs-down') | ||||
|           = downvotes | ||||
| 
 | ||||
|       - note_count = merge_request.mr_and_commit_notes.user.nonawards.count | ||||
|       - note_count = merge_request.mr_and_commit_notes.user.count | ||||
|       - if note_count > 0 | ||||
|         %li | ||||
|           = link_to merge_request_path(merge_request) + "#notes" do | ||||
|  |  | |||
|  | @ -50,7 +50,7 @@ | |||
|         %li.notes-tab | ||||
|           = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do | ||||
|             Discussion | ||||
|             %span.badge= @merge_request.mr_and_commit_notes.user.nonawards.count | ||||
|             %span.badge= @merge_request.mr_and_commit_notes.user.count | ||||
|         %li.commits-tab | ||||
|           = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do | ||||
|             Commits | ||||
|  | @ -68,7 +68,7 @@ | |||
|       .tab-content | ||||
|         #notes.notes.tab-pane.voting_notes | ||||
|           .content-block.content-block-small.oneline-block | ||||
|             = render 'votes/votes_block', votable: @merge_request | ||||
|             = render 'award_emoji/awards_block', awardable: @merge_request, inline: true | ||||
| 
 | ||||
|           .row | ||||
|             %section.col-md-12 | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| .awards.votes-block | ||||
|   - awards_sort(votable.notes.awards.grouped_awards).each do |emoji, notes| | ||||
|     %button.btn.award-control.js-emoji-btn.has-tooltip{class: (note_active_class(notes, current_user)), data: {placement: "top", original_title: emoji_author_list(notes, current_user)}} | ||||
| .awards.votes-block{data: { toggle_url: url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable]) }} | ||||
|   - awards_sort(awardable.grouped_awards).each do |emoji, awards| | ||||
|     %button.btn.award-control.js-emoji-btn.has-tooltip{class: (note_active_class(awards, current_user)), data: {placement: "top", original_title: emoji_author_list(awards, current_user)}} | ||||
|       = emoji_icon(emoji, sprite: false) | ||||
|       %span.award-control-text.js-counter | ||||
|         = notes.count | ||||
|         = awards.count | ||||
| 
 | ||||
|   - if current_user | ||||
|     %div.award-menu-holder.js-award-holder | ||||
|  |  | |||
|  | @ -8,3 +8,7 @@ | |||
| #   inflect.irregular 'person', 'people' | ||||
| #   inflect.uncountable %w( fish sheep ) | ||||
| # end | ||||
| # | ||||
| ActiveSupport::Inflector.inflections do |inflect| | ||||
|   inflect.uncountable %w(award_emoji) | ||||
| end | ||||
|  |  | |||
|  | @ -639,6 +639,7 @@ Rails.application.routes.draw do | |||
|             post :cancel_merge_when_build_succeeds | ||||
|             get :ci_status | ||||
|             post :toggle_subscription | ||||
|             post :toggle_award_emoji | ||||
|             post :remove_wip | ||||
|           end | ||||
| 
 | ||||
|  | @ -703,6 +704,7 @@ Rails.application.routes.draw do | |||
|         resources :issues, constraints: { id: /\d+/ } do | ||||
|           member do | ||||
|             post :toggle_subscription | ||||
|             post :toggle_award_emoji | ||||
|             get :referenced_merge_requests | ||||
|             get :related_branches | ||||
|           end | ||||
|  | @ -731,10 +733,7 @@ Rails.application.routes.draw do | |||
|         resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do | ||||
|           member do | ||||
|             delete :delete_attachment | ||||
|           end | ||||
| 
 | ||||
|           collection do | ||||
|             post :award_toggle | ||||
|             post :toggle_award_emoji | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,15 @@ | |||
| class AddAwardEmoji < ActiveRecord::Migration | ||||
|   def change | ||||
|     create_table :award_emoji do |t| | ||||
|       t.string :name | ||||
|       t.references :user | ||||
|       t.references :awardable, polymorphic: true | ||||
| 
 | ||||
|       t.timestamps | ||||
|     end | ||||
| 
 | ||||
|     add_index :award_emoji, :user_id | ||||
|     add_index :award_emoji, :awardable_type | ||||
|     add_index :award_emoji, :awardable_id | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,17 @@ | |||
| class ConvertAwardNoteToEmojiAward < ActiveRecord::Migration | ||||
|   def change | ||||
|     def up | ||||
|       execute "INSERT INTO award_emoji (awardable_type, awardable_id, user_id, name, created_at, updated_at) (SELECT noteable_type, noteable_id, author_id, note, created_at, updated_at FROM notes WHERE is_award = true)" | ||||
|     end | ||||
| 
 | ||||
|     def down | ||||
|       execute <<-SQL | ||||
|       INSERT INTO notes (noteable_type, noteable_id, author_id, note, created_at, updated_at, is_award)  | ||||
|         (SELECT awardable_type, awardable_id, user_id, name, created_at, updated_at, TRUE  | ||||
|          FROM award_emoji  | ||||
|          WHERE awardable_type IN ('Issue', 'MergeRequest') | ||||
|         ) | ||||
|       SQL | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,5 @@ | |||
| class RemoveNoteIsAward < ActiveRecord::Migration | ||||
|   def change | ||||
|     remove_column :notes, :is_award, :boolean | ||||
|   end | ||||
| end | ||||
							
								
								
									
										53
									
								
								db/schema.rb
								
								
								
								
							
							
						
						
									
										53
									
								
								db/schema.rb
								
								
								
								
							|  | @ -11,7 +11,7 @@ | |||
| # | ||||
| # It's strongly recommended that you check this file into your version control system. | ||||
| 
 | ||||
| ActiveRecord::Schema.define(version: 20160412140240) do | ||||
| ActiveRecord::Schema.define(version: 20160416190505) do | ||||
| 
 | ||||
|   # These are extensions that must be enabled in order to support this database | ||||
|   enable_extension "plpgsql" | ||||
|  | @ -94,6 +94,19 @@ ActiveRecord::Schema.define(version: 20160412140240) do | |||
|   add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree | ||||
|   add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree | ||||
| 
 | ||||
|   create_table "award_emoji", force: :cascade do |t| | ||||
|     t.string   "name" | ||||
|     t.integer  "user_id" | ||||
|     t.integer  "awardable_id" | ||||
|     t.string   "awardable_type" | ||||
|     t.datetime "created_at" | ||||
|     t.datetime "updated_at" | ||||
|   end | ||||
| 
 | ||||
|   add_index "award_emoji", ["awardable_id"], name: "index_award_emoji_on_awardable_id", using: :btree | ||||
|   add_index "award_emoji", ["awardable_type"], name: "index_award_emoji_on_awardable_type", using: :btree | ||||
|   add_index "award_emoji", ["user_id"], name: "index_award_emoji_on_user_id", using: :btree | ||||
| 
 | ||||
|   create_table "broadcast_messages", force: :cascade do |t| | ||||
|     t.text     "message",    null: false | ||||
|     t.datetime "starts_at" | ||||
|  | @ -622,14 +635,12 @@ ActiveRecord::Schema.define(version: 20160412140240) do | |||
|     t.boolean  "system",        default: false, null: false | ||||
|     t.text     "st_diff" | ||||
|     t.integer  "updated_by_id" | ||||
|     t.boolean  "is_award",      default: false, null: false | ||||
|   end | ||||
| 
 | ||||
|   add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree | ||||
|   add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree | ||||
|   add_index "notes", ["created_at", "id"], name: "index_notes_on_created_at_and_id", using: :btree | ||||
|   add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree | ||||
|   add_index "notes", ["is_award"], name: "index_notes_on_is_award", using: :btree | ||||
|   add_index "notes", ["line_code"], name: "index_notes_on_line_code", using: :btree | ||||
|   add_index "notes", ["note"], name: "index_notes_on_note_trigram", using: :gin, opclasses: {"note"=>"gin_trgm_ops"} | ||||
|   add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree | ||||
|  | @ -716,37 +727,37 @@ ActiveRecord::Schema.define(version: 20160412140240) do | |||
|     t.datetime "created_at" | ||||
|     t.datetime "updated_at" | ||||
|     t.integer  "creator_id" | ||||
|     t.boolean  "issues_enabled",         default: true,     null: false | ||||
|     t.boolean  "wall_enabled",           default: true,     null: false | ||||
|     t.boolean  "merge_requests_enabled", default: true,     null: false | ||||
|     t.boolean  "wiki_enabled",           default: true,     null: false | ||||
|     t.boolean  "issues_enabled",               default: true,     null: false | ||||
|     t.boolean  "wall_enabled",                 default: true,     null: false | ||||
|     t.boolean  "merge_requests_enabled",       default: true,     null: false | ||||
|     t.boolean  "wiki_enabled",                 default: true,     null: false | ||||
|     t.integer  "namespace_id" | ||||
|     t.string   "issues_tracker",         default: "gitlab", null: false | ||||
|     t.string   "issues_tracker",               default: "gitlab", null: false | ||||
|     t.string   "issues_tracker_id" | ||||
|     t.boolean  "snippets_enabled",       default: true,     null: false | ||||
|     t.boolean  "snippets_enabled",             default: true,     null: false | ||||
|     t.datetime "last_activity_at" | ||||
|     t.string   "import_url" | ||||
|     t.integer  "visibility_level",       default: 0,        null: false | ||||
|     t.boolean  "archived",               default: false,    null: false | ||||
|     t.integer  "visibility_level",             default: 0,        null: false | ||||
|     t.boolean  "archived",                     default: false,    null: false | ||||
|     t.string   "avatar" | ||||
|     t.string   "import_status" | ||||
|     t.float    "repository_size",        default: 0.0 | ||||
|     t.integer  "star_count",             default: 0,        null: false | ||||
|     t.float    "repository_size",              default: 0.0 | ||||
|     t.integer  "star_count",                   default: 0,        null: false | ||||
|     t.string   "import_type" | ||||
|     t.string   "import_source" | ||||
|     t.integer  "commit_count",           default: 0 | ||||
|     t.integer  "commit_count",                 default: 0 | ||||
|     t.text     "import_error" | ||||
|     t.integer  "ci_id" | ||||
|     t.boolean  "builds_enabled",         default: true,     null: false | ||||
|     t.boolean  "shared_runners_enabled", default: true,     null: false | ||||
|     t.boolean  "builds_enabled",               default: true,     null: false | ||||
|     t.boolean  "shared_runners_enabled",       default: true,     null: false | ||||
|     t.string   "runners_token" | ||||
|     t.string   "build_coverage_regex" | ||||
|     t.boolean  "build_allow_git_fetch",  default: true,     null: false | ||||
|     t.integer  "build_timeout",          default: 3600,     null: false | ||||
|     t.boolean  "pending_delete",         default: false | ||||
|     t.boolean  "public_builds",          default: true,     null: false | ||||
|     t.boolean  "build_allow_git_fetch",        default: true,     null: false | ||||
|     t.integer  "build_timeout",                default: 3600,     null: false | ||||
|     t.boolean  "pending_delete",               default: false | ||||
|     t.boolean  "public_builds",                default: true,     null: false | ||||
|     t.string   "main_language" | ||||
|     t.integer  "pushes_since_gc",        default: 0 | ||||
|     t.integer  "pushes_since_gc",              default: 0 | ||||
|     t.boolean  "last_repository_check_failed" | ||||
|     t.datetime "last_repository_check_at" | ||||
|   end | ||||
|  |  | |||
|  | @ -170,6 +170,7 @@ module API | |||
|       expose :label_names, as: :labels | ||||
|       expose :milestone, using: Entities::Milestone | ||||
|       expose :assignee, :author, using: Entities::UserBasic | ||||
|       expose :upvotes, :downvotes | ||||
| 
 | ||||
|       expose :subscribed do |issue, options| | ||||
|         issue.subscribed?(options[:current_user]) | ||||
|  | @ -178,7 +179,7 @@ module API | |||
| 
 | ||||
|     class MergeRequest < ProjectEntity | ||||
|       expose :target_branch, :source_branch | ||||
|       expose :upvotes,  :downvotes | ||||
|       expose :upvotes, :downvotes | ||||
|       expose :author, :assignee, using: Entities::UserBasic | ||||
|       expose :source_project_id, :target_project_id | ||||
|       expose :label_names, as: :labels | ||||
|  | @ -216,8 +217,8 @@ module API | |||
|       expose :system?, as: :system | ||||
|       expose :noteable_id, :noteable_type | ||||
|       # upvote? and downvote? are deprecated, always return false | ||||
|       expose :upvote?, as: :upvote | ||||
|       expose :downvote?, as: :downvote | ||||
|       expose(:upvote?)    { |note| false } | ||||
|       expose(:downvote?)  { |note| false } | ||||
|     end | ||||
| 
 | ||||
|     class MRNote < Grape::Entity | ||||
|  |  | |||
|  | @ -1,80 +0,0 @@ | |||
| class AwardEmoji | ||||
|   CATEGORIES = { | ||||
|     other: "Other", | ||||
|     objects: "Objects", | ||||
|     places: "Places", | ||||
|     travel_places: "Travel", | ||||
|     emoticons: "Emoticons", | ||||
|     objects_symbols: "Symbols", | ||||
|     nature: "Nature", | ||||
|     celebration: "Celebration", | ||||
|     people: "People", | ||||
|     activity: "Activity", | ||||
|     flags: "Flags", | ||||
|     food_drink: "Food" | ||||
|   }.with_indifferent_access | ||||
| 
 | ||||
|   CATEGORY_ALIASES = { | ||||
|     symbols: "objects_symbols", | ||||
|     foods: "food_drink", | ||||
|     travel: "travel_places" | ||||
|   }.with_indifferent_access | ||||
| 
 | ||||
|   def self.normilize_emoji_name(name) | ||||
|     aliases[name] || name | ||||
|   end | ||||
| 
 | ||||
|   def self.emoji_by_category | ||||
|     unless @emoji_by_category | ||||
|       @emoji_by_category = Hash.new { |h, key| h[key] = [] } | ||||
| 
 | ||||
|       emojis.each do |emoji_name, data| | ||||
|         data["name"] = emoji_name | ||||
| 
 | ||||
|         # Skip Fitzpatrick(tone) modifiers | ||||
|         next if data["category"] == "modifier" | ||||
| 
 | ||||
|         category = CATEGORY_ALIASES[data["category"]] || data["category"] | ||||
| 
 | ||||
|         @emoji_by_category[category] << data | ||||
|       end | ||||
| 
 | ||||
|       @emoji_by_category = @emoji_by_category.sort.to_h | ||||
|     end | ||||
| 
 | ||||
|     @emoji_by_category | ||||
|   end | ||||
| 
 | ||||
|   def self.emojis | ||||
|     @emojis ||= begin | ||||
|       json_path = File.join(Rails.root, 'fixtures', 'emojis', 'index.json' ) | ||||
|       JSON.parse(File.read(json_path)) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def self.aliases | ||||
|     @aliases ||= begin | ||||
|       json_path = File.join(Rails.root, 'fixtures', 'emojis', 'aliases.json' ) | ||||
|       JSON.parse(File.read(json_path)) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   # Returns an Array of Emoji names and their asset URLs. | ||||
|   def self.urls | ||||
|     @urls ||= begin | ||||
|       path = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json') | ||||
|       prefix = Gitlab::Application.config.assets.prefix | ||||
|       digest = Gitlab::Application.config.assets.digest | ||||
| 
 | ||||
|       JSON.parse(File.read(path)).map do |hash| | ||||
|         if digest | ||||
|           fname = "#{hash['unicode']}-#{hash['digest']}" | ||||
|         else | ||||
|           fname = hash['unicode'] | ||||
|         end | ||||
| 
 | ||||
|         { name: hash['name'], path: "#{prefix}/#{fname}.png" } | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,82 @@ | |||
| module Gitlab | ||||
|   class AwardEmoji | ||||
|     CATEGORIES = { | ||||
|       other: "Other", | ||||
|       objects: "Objects", | ||||
|       places: "Places", | ||||
|       travel_places: "Travel", | ||||
|       emoticons: "Emoticons", | ||||
|       objects_symbols: "Symbols", | ||||
|       nature: "Nature", | ||||
|       celebration: "Celebration", | ||||
|       people: "People", | ||||
|       activity: "Activity", | ||||
|       flags: "Flags", | ||||
|       food_drink: "Food" | ||||
|     }.with_indifferent_access | ||||
| 
 | ||||
|     CATEGORY_ALIASES = { | ||||
|       symbols: "objects_symbols", | ||||
|       foods: "food_drink", | ||||
|       travel: "travel_places" | ||||
|     }.with_indifferent_access | ||||
| 
 | ||||
|     def self.normalize_emoji_name(name) | ||||
|       aliases[name] || name | ||||
|     end | ||||
| 
 | ||||
|     def self.emoji_by_category | ||||
|       unless @emoji_by_category | ||||
|         @emoji_by_category = Hash.new { |h, key| h[key] = [] } | ||||
| 
 | ||||
|         emojis.each do |emoji_name, data| | ||||
|           data["name"] = emoji_name | ||||
| 
 | ||||
|           # Skip Fitzpatrick(tone) modifiers | ||||
|           next if data["category"] == "modifier" | ||||
| 
 | ||||
|           category = CATEGORY_ALIASES[data["category"]] || data["category"] | ||||
| 
 | ||||
|           @emoji_by_category[category] << data | ||||
|         end | ||||
| 
 | ||||
|         @emoji_by_category = @emoji_by_category.sort.to_h | ||||
|       end | ||||
| 
 | ||||
|       @emoji_by_category | ||||
|     end | ||||
| 
 | ||||
|     def self.emojis | ||||
|       @emojis ||= begin | ||||
|                     json_path = File.join(Rails.root, 'fixtures', 'emojis', 'index.json' ) | ||||
|                     JSON.parse(File.read(json_path)) | ||||
|                   end | ||||
|     end | ||||
| 
 | ||||
|     def self.aliases | ||||
|       @aliases ||= begin | ||||
|                      json_path = File.join(Rails.root, 'fixtures', 'emojis', 'aliases.json' ) | ||||
|                      JSON.parse(File.read(json_path)) | ||||
|                    end | ||||
|     end | ||||
| 
 | ||||
|     # Returns an Array of Emoji names and their asset URLs. | ||||
|     def self.urls | ||||
|       @urls ||= begin | ||||
|                   path = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json') | ||||
|                   prefix = Gitlab::Application.config.assets.prefix | ||||
|                   digest = Gitlab::Application.config.assets.digest | ||||
| 
 | ||||
|                   JSON.parse(File.read(path)).map do |hash| | ||||
|                     if digest | ||||
|                       fname = "#{hash['unicode']}-#{hash['digest']}" | ||||
|                     else | ||||
|                       fname = hash['unicode'] | ||||
|                     end | ||||
| 
 | ||||
|                     { name: hash['name'], path: "#{prefix}/#{fname}.png" } | ||||
|                   end | ||||
|                 end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -31,9 +31,9 @@ describe GroupsController do | |||
|     let(:issue_2) { create(:issue, project: project) } | ||||
| 
 | ||||
|     before do | ||||
|       create_list(:upvote_note, 3, project: project, noteable: issue_2) | ||||
|       create_list(:upvote_note, 2, project: project, noteable: issue_1) | ||||
|       create_list(:downvote_note, 2, project: project, noteable: issue_2) | ||||
|       create_list(:award_emoji, 3, awardable: issue_2) | ||||
|       create_list(:award_emoji, 2, awardable: issue_1) | ||||
|       create_list(:award_emoji, 2, awardable: issue_2, name: "thumbsdown") | ||||
| 
 | ||||
|       sign_in(user) | ||||
|     end | ||||
|  | @ -56,9 +56,9 @@ describe GroupsController do | |||
|     let(:merge_request_2) { create(:merge_request, :simple, source_project: project) } | ||||
| 
 | ||||
|     before do | ||||
|       create_list(:upvote_note, 3, project: project, noteable: merge_request_2) | ||||
|       create_list(:upvote_note, 2, project: project, noteable: merge_request_1) | ||||
|       create_list(:downvote_note, 2, project: project, noteable: merge_request_2) | ||||
|       create_list(:award_emoji, 3, awardable: merge_request_2) | ||||
|       create_list(:award_emoji, 2, awardable: merge_request_1) | ||||
|       create_list(:award_emoji, 2, awardable: merge_request_2, name: "thumbsdown") | ||||
| 
 | ||||
|       sign_in(user) | ||||
|     end | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| FactoryGirl.define do | ||||
|   factory :award_emoji do | ||||
|     name "thumbsup" | ||||
|     user | ||||
|     awardable factory: :issue | ||||
|   end | ||||
| end | ||||
|  | @ -36,8 +36,6 @@ FactoryGirl.define do | |||
|     factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff] | ||||
|     factory :note_on_project_snippet,    traits: [:on_project_snippet] | ||||
|     factory :system_note,                traits: [:system] | ||||
|     factory :downvote_note,              traits: [:award, :downvote] | ||||
|     factory :upvote_note,                traits: [:award, :upvote] | ||||
| 
 | ||||
|     trait :on_commit do | ||||
|       project | ||||
|  | @ -69,10 +67,6 @@ FactoryGirl.define do | |||
|       system true | ||||
|     end | ||||
| 
 | ||||
|     trait :award do | ||||
|       is_award true | ||||
|     end | ||||
| 
 | ||||
|     trait :downvote do | ||||
|       note "thumbsdown" | ||||
|     end | ||||
|  |  | |||
|  | @ -127,18 +127,15 @@ describe IssuesHelper do | |||
|     it { is_expected.to eq("!1, !2, or !3") } | ||||
|   end | ||||
| 
 | ||||
|   describe "note_active_class" do | ||||
|     before do | ||||
|       @note = create :note | ||||
|       @note1 = create :note | ||||
|     end | ||||
|   describe '#award_active_class' do | ||||
|     let!(:upvote) { create(:award_emoji) } | ||||
| 
 | ||||
|     it "returns empty string for unauthenticated user" do | ||||
|       expect(note_active_class(Note.all, nil)).to eq("") | ||||
|       expect(award_active_class(AwardEmoji.all, nil)).to eq("") | ||||
|     end | ||||
| 
 | ||||
|     it "returns active string for author" do | ||||
|       expect(note_active_class(Note.all, @note.author)).to eq("active") | ||||
|       expect(award_active_class(AwardEmoji.all, upvote.user)).to eq("active") | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| require 'spec_helper' | ||||
| 
 | ||||
| describe AwardEmoji do | ||||
| describe Gitlab::AwardEmoji do | ||||
|   describe '.urls' do | ||||
|     subject { AwardEmoji.urls } | ||||
|     subject { Gitlab::AwardEmoji.urls } | ||||
| 
 | ||||
|     it { is_expected.to be_an_instance_of(Array) } | ||||
|     it { is_expected.to_not be_empty } | ||||
|  | @ -19,7 +19,7 @@ describe AwardEmoji do | |||
| 
 | ||||
|   describe '.emoji_by_category' do | ||||
|     it "only contains known categories" do | ||||
|       undefined_categories = AwardEmoji.emoji_by_category.keys - AwardEmoji::CATEGORIES.keys | ||||
|       undefined_categories = Gitlab::AwardEmoji.emoji_by_category.keys - Gitlab::AwardEmoji::CATEGORIES.keys | ||||
|       expect(undefined_categories).to be_empty | ||||
|     end | ||||
|   end | ||||
|  | @ -0,0 +1,31 @@ | |||
| require 'spec_helper' | ||||
| 
 | ||||
| describe AwardEmoji, models: true do | ||||
|   describe 'Associations' do | ||||
|     it { is_expected.to belong_to(:awardable) } | ||||
|     it { is_expected.to belong_to(:user) } | ||||
|   end | ||||
| 
 | ||||
|   describe 'modules' do | ||||
|     it { is_expected.to include_module(Participable) } | ||||
|   end | ||||
| 
 | ||||
|   describe "validations" do | ||||
|     it { is_expected.to validate_presence_of(:awardable) } | ||||
|     it { is_expected.to validate_presence_of(:user) } | ||||
|     it { is_expected.to validate_presence_of(:name) } | ||||
|     it { is_expected.to validate_presence_of(:awardable) } | ||||
| 
 | ||||
|     # To circumvent a bug in the shoulda matchers | ||||
|     describe "scoped uniqueness validation" do | ||||
|       it "rejects duplicate award emoji" do | ||||
|         user  = create(:user) | ||||
|         issue = create(:issue) | ||||
|         create(:award_emoji, user: user, awardable: issue) | ||||
|         new_award = AwardEmoji.new(user: user, awardable: issue, name: "thumbsup") | ||||
| 
 | ||||
|         expect(new_award).not_to be_valid | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -198,18 +198,4 @@ describe Issue, "Issuable" do | |||
|         to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' }) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe "votes" do | ||||
|     before do | ||||
|       author = create :user | ||||
|       project = create :empty_project | ||||
|       issue.notes.awards.create!(note: "thumbsup", author: author, project: project) | ||||
|       issue.notes.awards.create!(note: "thumbsdown", author: author, project: project) | ||||
|     end | ||||
| 
 | ||||
|     it "returns correct values" do | ||||
|       expect(issue.upvotes).to eq(1) | ||||
|       expect(issue.downvotes).to eq(1) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -152,23 +152,6 @@ describe Note, models: true do | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '.grouped_awards' do | ||||
|     before do | ||||
|       create :note, note: "smile", is_award: true | ||||
|       create :note, note: "smile", is_award: true | ||||
|     end | ||||
| 
 | ||||
|     it "returns grouped hash of notes" do | ||||
|       expect(Note.grouped_awards.keys.size).to eq(3) | ||||
|       expect(Note.grouped_awards["smile"]).to match_array(Note.all) | ||||
|     end | ||||
| 
 | ||||
|     it "returns thumbsup and thumbsdown always" do | ||||
|       expect(Note.grouped_awards["thumbsup"]).to match_array(Note.none) | ||||
|       expect(Note.grouped_awards["thumbsdown"]).to match_array(Note.none) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#active?' do | ||||
|     it 'is always true when the note has no associated diff' do | ||||
|       note = build(:note) | ||||
|  | @ -239,11 +222,6 @@ describe Note, models: true do | |||
|       note = build(:note, system: true) | ||||
|       expect(note.editable?).to be_falsy | ||||
|     end | ||||
| 
 | ||||
|     it "returns false" do | ||||
|       note = build(:note, is_award: true, note: "smiley") | ||||
|       expect(note.editable?).to be_falsy | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe "cross_reference_not_visible_for?" do | ||||
|  | @ -270,23 +248,6 @@ describe Note, models: true do | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe "set_award!" do | ||||
|     let(:merge_request) { create :merge_request } | ||||
| 
 | ||||
|     it "converts aliases to actual name" do | ||||
|       note = create(:note, note: ":+1:", noteable: merge_request) | ||||
|       expect(note.reload.note).to eq("thumbsup") | ||||
|     end | ||||
| 
 | ||||
|     it "is not an award emoji when comment is on a diff" do | ||||
|       note = create(:note, note: ":blowfish:", noteable: merge_request, line_code: "11d5d2e667e9da4f7f610f81d86c974b146b13bd_0_2") | ||||
|       note = note.reload | ||||
| 
 | ||||
|       expect(note.note).to eq(":blowfish:") | ||||
|       expect(note.is_award?).to be_falsy | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe 'clear_blank_line_code!' do | ||||
|     it 'clears a blank line code before validation' do | ||||
|       note = build(:note, line_code: ' ') | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue