diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/awards_handler.coffee | 55 | ||||
-rw-r--r-- | app/assets/javascripts/notes.js.coffee | 9 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/issuable.scss | 42 | ||||
-rw-r--r-- | app/controllers/projects/issues_controller.rb | 2 | ||||
-rw-r--r-- | app/controllers/projects/merge_requests_controller.rb | 2 | ||||
-rw-r--r-- | app/controllers/projects/notes_controller.rb | 25 | ||||
-rw-r--r-- | app/helpers/issues_helper.rb | 5 | ||||
-rw-r--r-- | app/models/concerns/issuable.rb | 49 | ||||
-rw-r--r-- | app/models/note.rb | 46 | ||||
-rw-r--r-- | app/services/notes/create_service.rb | 17 | ||||
-rw-r--r-- | app/services/notification_service.rb | 1 | ||||
-rw-r--r-- | app/views/projects/issues/_issue.html.haml | 2 | ||||
-rw-r--r-- | app/views/projects/merge_requests/_merge_request.html.haml | 2 | ||||
-rw-r--r-- | app/views/projects/notes/_note.html.haml | 20 | ||||
-rw-r--r-- | app/views/votes/_votes_block.html.haml | 43 | ||||
-rw-r--r-- | app/views/votes/_votes_inline.html.haml | 9 |
16 files changed, 189 insertions, 140 deletions
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee new file mode 100644 index 00000000000..aab3179f10e --- /dev/null +++ b/app/assets/javascripts/awards_handler.coffee @@ -0,0 +1,55 @@ +class @AwardsHandler + constructor: (@post_emoji_url, @noteable_type, @noteable_id) -> + + addAward: (emoji) -> + @postEmoji emoji, => + if @exist(emoji) + if @isActive(emoji) + @decrementCounter(emoji) + else + counter = $(".icon." + emoji).siblings(".counter") + counter.text(parseInt(counter.text()) + 1) + counter.parent().addClass("active") + else + @createEmoji(emoji) + + + exist: (emoji) -> + $(".icon").hasClass(emoji) + + isActive: (emoji) -> + $(".icon." + emoji).parent().hasClass("active") + + decrementCounter: (emoji) -> + counter = $(".icon." + emoji).siblings(".counter") + + if parseInt(counter.text()) > 1 + counter.text(parseInt(counter.text()) - 1) + counter.parent().removeClass("active") + else + counter.parent().remove() + + + createEmoji: (emoji) -> + nodes = [] + nodes.push("<div class='award active'>") + nodes.push("<div class='icon " + emoji + "'>") + nodes.push(@getImage(emoji)) + nodes.push("</div>") + nodes.push("<div class='counter'>1") + nodes.push("</div></div>") + + $(".awards").append(nodes.join("\n")) + + getImage: (emoji) -> + $("li." + emoji).html() + + postEmoji: (emoji, callback) -> + emoji = emoji.replace("emoji-", "") + $.post @post_emoji_url, { + emoji: emoji + noteable_type: @noteable_type + noteable_id: @noteable_id + },(data) -> + if data.ok + callback.call()
\ No newline at end of file diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index ea75c656bcc..cd27b20dd7c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -113,13 +113,16 @@ class @Notes renderNote: (note) -> # render note if it not present in loaded list # or skip if rendered - if @isNewNote(note) + if @isNewNote(note) && !note.award @note_ids.push(note.id) $('ul.main-notes-list'). append(note.html). syntaxHighlight() @initTaskList() + if note.award + awards_handler.addAward("emoji-" + note.note) + ### Check if note does not exists on page ### @@ -255,7 +258,6 @@ class @Notes ### addNote: (xhr, note, status) => @renderNote(note) - @updateVotes() ### Called in response to the new note form being submitted @@ -473,9 +475,6 @@ class @Notes form = $(e.target).closest(".js-discussion-note-form") @removeDiscussionNoteForm(form) - updateVotes: -> - true - ### Called after an attachment file has been selected. diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index abc27a19e32..efeb2393165 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -101,3 +101,45 @@ background-color: $background-color; } } + +.awards { + .award { + border: 1px solid; + padding: 1px 3px; + width: 50px; + border-radius: 5px; + float:left; + margin: 0 3px; + border-color: #ccc; + cursor: pointer; + + &.active { + border-color: rgba(79,176,252,.4); + background-color: rgba(79,176,252,.08); + + .counter { + font-weight: bold; + } + } + + .icon { + float: left; + margin-right: 10px; + } + } + + #add-award { + font-size: 20px; + border-radius: 5px; + float: left; + width: 50px; + font-weight: bold; + } + + .awards-menu{ + li { + float: left; + margin: 3px; + } + } +} diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index e74c2905e48..5250a0f5e67 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -60,7 +60,7 @@ class Projects::IssuesController < Projects::ApplicationController def show @participants = @issue.participants(current_user) @note = @project.notes.new(noteable: @issue) - @notes = @issue.notes.with_associations.fresh + @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue respond_with(@issue) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 188f0cc4cea..a0468c65d5a 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -254,7 +254,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) - @notes = @merge_request.mr_and_commit_notes.inc_author.fresh + @notes = @merge_request.nonawards.mr_and_commit_notes.inc_author.fresh @discussions = Note.discussions_from_notes(@notes) @noteable = @merge_request diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 41cd08c93c6..357b292980d 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -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] + before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle]] def index current_fetched_at = Time.now.to_i @@ -58,6 +58,27 @@ class Projects::NotesController < Projects::ApplicationController end end + def award_toggle + noteable = params[:noteable_type] == "Issue" ? Issue : MergeRequest + noteable = noteable.find(params[:noteable_id]) + data = { + noteable: noteable, + author: current_user, + is_award: true, + note: params[:emoji] + } + + note = project.notes.find_by(data) + + if note + note.destroy + else + project.notes.create(data) + end + + render json: {ok: true} + end + private def note @@ -111,6 +132,8 @@ class Projects::NotesController < Projects::ApplicationController id: note.id, discussion_id: note.discussion_id, html: note_to_html(note), + award: note.is_award, + note: note.note, discussion_html: note_to_discussion_html(note), discussion_with_diff_html: note_to_discussion_with_diff_html(note) } diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index beb083d82dc..ff3e0911954 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -87,6 +87,11 @@ module IssuesHelper merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ') end + def url_to_emoji(name) + emoji_path = "emoji/#{Emoji.emoji_filename(name)}.png" + url_to_image(emoji_path) + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 492a026add9..91da6797df7 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -89,41 +89,6 @@ module Issuable opened? || reopened? end - # - # Votes - # - - # Return the number of -1 comments (downvotes) - def downvotes - filter_superceded_votes(notes.select(&:downvote?), notes).size - end - - def downvotes_in_percent - if votes_count.zero? - 0 - else - 100.0 - upvotes_in_percent - end - end - - # Return the number of +1 comments (upvotes) - def upvotes - filter_superceded_votes(notes.select(&:upvote?), notes).size - end - - def upvotes_in_percent - if votes_count.zero? - 0 - else - 100.0 / votes_count * upvotes - end - end - - # Return the total number of votes - def votes_count - upvotes + downvotes - end - def subscribed?(user) subscription = subscriptions.find_by_user_id(user.id) @@ -183,18 +148,4 @@ module Issuable def notes_with_associations notes.includes(:author, :project) end - - private - - def filter_superceded_votes(votes, notes) - filteredvotes = [] + votes - - votes.each do |vote| - if vote.superceded?(notes) - filteredvotes.delete(vote) - end - end - - filteredvotes - end end diff --git a/app/models/note.rb b/app/models/note.rb index 0b3aa30abd7..458d433211c 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -50,6 +50,8 @@ class Note < ActiveRecord::Base mount_uploader :attachment, AttachmentUploader # Scopes + scope :awards, ->{ where("is_award IS TRUE") } + scope :nonawards, ->{ where("is_award IS 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, '']) } @@ -97,6 +99,12 @@ class Note < ActiveRecord::Base def search(query) where("LOWER(note) like :query", query: "%#{query.downcase}%") end + + def grouped_awards + select(:note).distinct.map do |note| + [ note.note, where(note: note.note) ] + end + end end def cross_reference? @@ -288,44 +296,6 @@ class Note < ActiveRecord::Base nil end - DOWNVOTES = %w(-1 :-1: :thumbsdown: :thumbs_down_sign:) - - # Check if the note is a downvote - def downvote? - votable? && note.start_with?(*DOWNVOTES) - end - - UPVOTES = %w(+1 :+1: :thumbsup: :thumbs_up_sign:) - - # Check if the note is an upvote - def upvote? - votable? && note.start_with?(*UPVOTES) - end - - def superceded?(notes) - return false unless vote? - - notes.each do |note| - next if note == self - - if note.vote? && - self[:author_id] == note[:author_id] && - self[:created_at] <= note[:created_at] - return true - end - end - - false - end - - def vote? - upvote? || downvote? - end - - def votable? - for_issue? || (for_merge_request? && !for_diff_line?) - end - # Mentionable override. def gfm_reference(from_project = nil) noteable.gfm_reference(from_project) diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 2001dc89c33..f448f61cc86 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -5,11 +5,16 @@ module Notes note.author = current_user note.system = false + if contains_emoji_only?(params[:note]) + note.is_award = true + note.note = emoji_name(params[:note]) + end + if note.save notification_service.new_note(note) - # Skip system notes, like status changes and cross-references. - unless note.system + # Skip system notes, like status changes and cross-references and awards + unless note.system || note.is_award event_service.leave_note(note, note.author) note.create_cross_references! execute_hooks(note) @@ -28,5 +33,13 @@ module Notes note.project.execute_hooks(note_data, :note_hooks) note.project.execute_services(note_data, :note_hooks) end + + def contains_emoji_only?(note) + note =~ /^:[-_+[:alnum:]]*:\s?/ + end + + def emoji_name(note) + note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] + end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index bbfe755f44a..d6550fbb555 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -102,6 +102,7 @@ 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 diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 55ce912829d..d7657ee7e40 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -29,8 +29,6 @@ .issue-info = "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe - - if issue.votes_count > 0 - = render 'votes/votes_inline', votable: issue - if issue.milestone %span diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index c5234c0618c..83e8ad11989 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -34,8 +34,6 @@ .merge-request-info = "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe - - if merge_request.votes_count > 0 - = render 'votes/votes_inline', votable: merge_request - if merge_request.milestone_id? %span diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index efa7dd01cc2..dd0abc8c746 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -35,26 +35,6 @@ - if note.updated_by && note.updated_by != note.author by #{link_to_member(note.project, note.updated_by, avatar: false, author_class: nil)} - - if note.superceded?(@notes) - - if note.upvote? - %span.vote.upvote.label.label-gray.strikethrough - = icon('thumbs-up') - \+1 - - if note.downvote? - %span.vote.downvote.label.label-gray.strikethrough - = icon('thumbs-down') - \-1 - - else - - if note.upvote? - %span.vote.upvote.label.label-success - = icon('thumbs-up') - \+1 - - if note.downvote? - %span.vote.downvote.label.label-danger - = icon('thumbs-down') - \-1 - - .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''} .note-text = preserve do diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 36ea6742064..3c3ae9dd0b2 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,10 +1,33 @@ -.votes.votes-block - .btn-group - - unless votable.upvotes.zero? - .btn.btn-sm.disabled.cgreen - %i.fa.fa-thumbs-up - = votable.upvotes - - unless votable.downvotes.zero? - .btn.btn-sm.disabled.cred - %i.fa.fa-thumbs-down - = votable.downvotes +.awards.votes-block + - votable.notes.awards.grouped_awards.each do | vote | + .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id))} + .icon{class: "emoji-#{vote.first}"} + = image_tag url_to_emoji(vote.first), height: "20px", width: "20px" + .counter + = vote.last.count + + %button.dropdown + %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + + %ul.dropdown-menu.awards-menu + - ["100", "blush", "heart", "smile", "rage", "beers", "thumbsup", "disappointed", "ok_hand", "helicopter"].each do |emoji| + %li{class: "emoji-#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" + + +:coffeescript + post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" + noteable_type = "Issue" + noteable_id = #{@issue.id} + awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) + + $ -> + $(".awards-menu li").click (e)-> + emoji = $(this).attr("class") + awards_handler.addAward(emoji) + + $(".awards").on "click", ".award", (e)-> + emoji = /(emoji-\S*)/.exec($(this).find(".icon").attr("class"))[0] + awards_handler.addAward(emoji) + + + +
\ No newline at end of file diff --git a/app/views/votes/_votes_inline.html.haml b/app/views/votes/_votes_inline.html.haml deleted file mode 100644 index 2cb3ae04e1a..00000000000 --- a/app/views/votes/_votes_inline.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -.votes.votes-inline - - unless votable.upvotes.zero? - %span.upvotes.cgreen - + #{votable.upvotes} - - unless votable.downvotes.zero? - \/ - - unless votable.downvotes.zero? - %span.downvotes.cred - \- #{votable.downvotes} |