Commit fd2c0fe4 authored by Valery Sizov's avatar Valery Sizov
Browse files

award emoji

parent b13bdba4c92d
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
......@@ -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.
......
......@@ -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;
}
}
}
......@@ -60,7 +60,7 @@ def edit
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)
......
......@@ -254,7 +254,7 @@ 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.inc_author.fresh
@notes = @merge_request.nonawards.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]
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 @@ def delete_attachment
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 @@ def render_note_json(note)
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)
}
......
......@@ -87,6 +87,11 @@ def merge_requests_sentence(merge_requests)
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
......@@ -89,41 +89,6 @@ def open?
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 @@ def to_ability_name
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
......@@ -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 @@ def build_discussion_id(type, id, line_code)
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 @@ def noteable
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)
......
......@@ -5,11 +5,16 @@ def execute
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 @@ def execute_hooks(note)
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
......@@ -102,6 +102,7 @@ def new_note(note)
# 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
......
......@@ -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
&nbsp;
%span
......
......@@ -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?
&nbsp;
%span
......
......@@ -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
......
.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
.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}
......@@ -664,6 +664,10 @@
member do
delete :delete_attachment
end
collection do
post :award_toggle
end
end
resources :uploads, only: [:create] do
......
class AddIsAwardToNotes < ActiveRecord::Migration
def change
add_column :notes, :is_award, :boolean, default: false
end
end
......@@ -531,7 +531,7 @@
t.string "type"
t.string "description", default: "", null: false
t.string "avatar"
t.boolean "public", default: false
t.boolean "visible", default: false
end
add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree
......@@ -554,6 +554,7 @@
t.boolean "system", default: false, null: false
t.text "st_diff"
t.integer "updated_by_id"
t.boolean "is_award", default: false
end
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
......
......@@ -31,8 +31,6 @@ Parameters:
"project_id": 3,
"title": "test1",
"state": "opened",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"username": "admin",
......@@ -77,8 +75,6 @@ Parameters:
"project_id": 3,
"title": "test1",
"state": "merged",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"username": "admin",
......@@ -126,8 +122,6 @@ Parameters:
"updated_at": "2015-02-02T20:08:49.959Z",
"target_branch": "secret_token",
"source_branch": "version-1-9",
"upvotes": 0,
"downvotes": 0,
"author": {
"name": "Chad Hamill",
"username": "jarrett",
......@@ -198,8 +192,6 @@ Parameters:
"project_id": 3,
"title": "test1",
"state": "opened",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"username": "admin",
......@@ -250,8 +242,6 @@ Parameters:
"title": "test1",
"description": "description1",
"state": "opened",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"username": "admin",
......@@ -304,8 +294,6 @@ Parameters:
"project_id": 3,
"title": "test1",
"state": "merged",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"username": "admin",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment