diff options
-rw-r--r-- | app/assets/javascripts/awards_handler.coffee | 215 | ||||
-rw-r--r-- | app/assets/javascripts/dispatcher.js.coffee | 2 | ||||
-rw-r--r-- | app/assets/javascripts/lib/emoji_aliases.js.coffee.erb | 9 | ||||
-rw-r--r-- | app/assets/javascripts/notes.js.coffee | 4 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/awards.scss | 13 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/notes.scss | 41 | ||||
-rw-r--r-- | app/finders/notes_finder.rb | 4 | ||||
-rw-r--r-- | app/views/award_emoji/_awards_block.html.haml | 6 |
8 files changed, 192 insertions, 102 deletions
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index af4462ece38..4c0a274b793 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -1,63 +1,109 @@ class @AwardsHandler - constructor: (@get_emojis_url, @post_emoji_url, @noteable_type, @noteable_id, @aliases) -> - $(".js-add-award").on "click", (event) => - event.stopPropagation() - event.preventDefault() + constructor: -> + @aliases = gl.emoji.emojiAliases() - @showEmojiMenu() + $(document) + .off "click", ".js-add-award" + .on "click", ".js-add-award", (event) => + event.stopPropagation() + event.preventDefault() + + @showEmojiMenu $(event.currentTarget) $("html").on 'click', (event) -> if !$(event.target).closest(".emoji-menu").length if $(".emoji-menu").is(":visible") + $('.js-add-award.is-active').removeClass 'is-active' $(".emoji-menu").removeClass "is-visible" - $(".awards") - .off "click" - .on "click", ".js-emoji-btn", @handleClick - - @renderFrequentlyUsedBlock() + $(document) + .off "click", ".js-emoji-btn" + .on "click", ".js-emoji-btn", (e) => @handleClick(e) handleClick: (e) -> e.preventDefault() - emoji = $(this) + $emojiBtn = $(e.currentTarget) + $addAwardBtn = $('.js-add-award.is-active') + $votesBlock = $($addAwardBtn.closest('.js-award-holder').data('target')) + + if $addAwardBtn.length is 0 + $votesBlock = $emojiBtn.closest('.js-awards-block') + else if $votesBlock.length is 0 + $votesBlock = $addAwardBtn.closest('.js-awards-block') + + $votesBlock.addClass 'js-awards-block-current' + awardUrl = $votesBlock.data 'award-url' + emoji = $emojiBtn .find(".icon") .data "emoji" - if emoji is "thumbsup" and awards_handler.didUserClickEmoji $(this), "thumbsdown" - awards_handler.addAward "thumbsdown" + if emoji is "thumbsup" and @didUserClickEmoji $emojiBtn, "thumbsdown" + @addAward awardUrl, "thumbsdown" + + else if emoji is "thumbsdown" and @didUserClickEmoji $emojiBtn, "thumbsup" + @addAward awardUrl, "thumbsup" - else if emoji is "thumbsdown" and awards_handler.didUserClickEmoji $(this), "thumbsup" - awards_handler.addAward "thumbsup" + @addAward awardUrl, emoji - awards_handler.addAward emoji + didUserClickEmoji: (emojiBtn, emoji) -> + if emojiBtn.siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title") + emojiBtn.siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title").indexOf('me') > -1 - didUserClickEmoji: (that, emoji) -> - if $(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title") - $(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title").indexOf('me') > -1 + showEmojiMenu: ($addBtn) -> + $menu = $('.emoji-menu') + if $menu.length + $holder = $addBtn.closest('.js-award-holder') - showEmojiMenu: -> - if $(".emoji-menu").length - if $(".emoji-menu").is ".is-visible" - $(".emoji-menu").removeClass "is-visible" + if $menu.is ".is-visible" + $addBtn.removeClass "is-active" + $menu.removeClass "is-visible" $("#emoji_search").blur() else $(".emoji-menu").addClass "is-visible" + $addBtn.addClass "is-active" + @positionMenu($menu, $addBtn) + + $menu.addClass "is-visible" $("#emoji_search").focus() else - $('.js-add-award').addClass "is-loading" - $.get @get_emojis_url, (response) => - $('.js-add-award').removeClass "is-loading" - $(".js-award-holder").append response + $addBtn.addClass "is-loading is-active" + $.get $addBtn.data('award-menu-url'), (response) => + $addBtn.removeClass "is-loading" + $('body').append response + + $menu = $(".emoji-menu") + + @positionMenu($menu, $addBtn) + + @renderFrequentlyUsedBlock() setTimeout => - $(".emoji-menu").addClass "is-visible" + $menu.addClass "is-visible" $("#emoji_search").focus() @setupSearch() , 200 - addAward: (emoji) -> + positionMenu: ($menu, $addBtn) -> + position = $addBtn.data('position') + + # The menu could potentially be off-screen or in a hidden overflow element + # So we position the element absolute in the body + css = + top: "#{$addBtn.offset().top + $addBtn.outerHeight()}px" + + if position? and position is 'right' + css.left = "#{($addBtn.offset().left - $menu.outerWidth()) + 20}px" + $menu.addClass "is-aligned-right" + else + css.left = "#{$addBtn.offset().left}px" + $menu.removeClass "is-aligned-right" + + $menu.css(css) + + addAward: (awardUrl, emoji) -> emoji = @normilizeEmojiName(emoji) - @postEmoji emoji, => + @postEmoji awardUrl, emoji, => @addAwardToEmojiBar(emoji) + $('.js-awards-block').removeClass 'js-awards-block-current' $(".emoji-menu").removeClass "is-visible" @@ -65,58 +111,60 @@ class @AwardsHandler @addEmojiToFrequentlyUsedList(emoji) emoji = @normilizeEmojiName(emoji) - if @exist(emoji) - if @isActive(emoji) - @decrementCounter(emoji) + $emojiBtn = @findEmojiIcon(emoji).parent() + + if $emojiBtn.length > 0 + if @isActive($emojiBtn) + @decrementCounter($emojiBtn, emoji) else - counter = @findEmojiIcon(emoji).siblings(".js-counter") - counter.text(parseInt(counter.text()) + 1) - counter.parent().addClass("active") - @addMeToAuthorList(emoji) + $counter = $emojiBtn.find('.js-counter') + $counter.text(parseInt($counter.text()) + 1) + $emojiBtn.addClass("active") + @addMeToUserList(emoji) else @createEmoji(emoji) - exist: (emoji) -> - @findEmojiIcon(emoji).length > 0 - - isActive: (emoji) -> - @findEmojiIcon(emoji).parent().hasClass("active") - - decrementCounter: (emoji) -> - counter = @findEmojiIcon(emoji).siblings(".js-counter") - emojiIcon = counter.parent() - if parseInt(counter.text()) > 1 - counter.text(parseInt(counter.text()) - 1) - emojiIcon.removeClass("active") - @removeMeFromAuthorList(emoji) - else if emoji == "thumbsup" || emoji == "thumbsdown" - emojiIcon.tooltip("destroy") - counter.text(0) - emojiIcon.removeClass("active") - @removeMeFromAuthorList(emoji) + isActive: ($emojiBtn) -> + $emojiBtn.hasClass("active") + + decrementCounter: ($emojiBtn, emoji) -> + $awardsBlock = $emojiBtn.closest('.js-awards-block') + isntNoteBody = $emojiBtn.closest('.note-body').length is 0 + counter = $('.js-counter', $emojiBtn) + counterNumber = parseInt(counter.text()) + + if counterNumber > 1 + counter.text(counterNumber - 1) + @removeMeFromUserList($emojiBtn, emoji) + else if (emoji == "thumbsup" || emoji == "thumbsdown") && isntNoteBody + $emojiBtn.tooltip("destroy") + counter.text('0') + @removeMeFromUserList($emojiBtn, emoji) else - emojiIcon.tooltip("destroy") - emojiIcon.remove() + $emojiBtn.tooltip("destroy") + $emojiBtn.remove() - removeMeFromAuthorList: (emoji) -> - award_block = @findEmojiIcon(emoji).parent() + $emojiBtn.removeClass("active") + + removeMeFromUserList: ($emojiBtn, emoji) -> + award_block = $emojiBtn authors = award_block .attr("data-original-title") .split(", ") - authors.splice(authors.indexOf("me"),1) + authors.splice(authors.indexOf("me"), 1) award_block .closest(".js-emoji-btn") .attr("data-original-title", authors.join(", ")) @resetTooltip(award_block) - addMeToAuthorList: (emoji) -> + addMeToUserList: (emoji) -> award_block = @findEmojiIcon(emoji).parent() origTitle = award_block.attr("data-original-title").trim() - authors = [] + users = [] if origTitle - authors = origTitle.split(', ') - authors.push("me") - award_block.attr("data-original-title", authors.join(", ")) + users = origTitle.split(', ') + users.push("me") + award_block.attr("data-original-title", users.join(", ")) @resetTooltip(award_block) resetTooltip: (award) -> @@ -127,24 +175,24 @@ class @AwardsHandler award.tooltip() ), 200 - createEmoji: (emoji) -> emojiCssClass = @resolveNameToCssClass(emoji) - nodes = [] - nodes.push( - "<button class='btn award-control js-emoji-btn has-tooltip active' data-original-title='me'>", - "<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>", - "<span class='award-control-text js-counter'>1</span>", - "</button>" - ) + buttonHtml = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'> + <div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div> + <span class='award-control-text js-counter'>1</span> + </button>" - emoji_node = $(nodes.join("\n")) - .insertBefore(".js-award-holder") + emoji_node = $(buttonHtml) + .insertBefore(".js-awards-block-current .js-award-holder:not(.js-award-action-btn)") .find(".emoji-icon") .data("emoji", emoji) $('.award-control').tooltip() + $currentBlock = $('.js-awards-block-current') + if $currentBlock.is('.hidden') + $currentBlock.removeClass 'hidden' + resolveNameToCssClass: (emoji) -> emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']") @@ -156,17 +204,13 @@ class @AwardsHandler "emoji-#{unicodeName}" - postEmoji: (emoji, callback) -> - $.post @post_emoji_url, { note: { - note: ":#{emoji}:" - noteable_type: @noteable_type - noteable_id: @noteable_id - }},(data) -> + postEmoji: (awardUrl, emoji, callback) -> + $.post awardUrl, { name: emoji }, (data) -> if data.ok callback.call() findEmojiIcon: (emoji) -> - $(".awards > .js-emoji-btn [data-emoji='#{emoji}']") + $(".js-awards-block-current.awards > .js-emoji-btn [data-emoji='#{emoji}']") scrollToAwards: -> $('body, html').animate({ @@ -189,16 +233,15 @@ class @AwardsHandler if $.cookie('frequently_used_emojis') frequently_used_emojis = @getFrequentlyUsedEmojis() - ul = $("<ul>") + ul = $("<ul class='clearfix emoji-menu-list'>") for emoji in frequently_used_emojis - do (emoji) -> - $(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul) + $(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul) $("input.emoji-search").after(ul).after($("<h5>").text("Frequently used")) setupSearch: -> - $("input.emoji-search").keyup (ev) => + $("input.emoji-search").on 'keyup', (ev) => term = $(ev.target).val() # Clean previous search results diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 0b9110d35fa..610fa990fc7 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -22,6 +22,7 @@ class Dispatcher new Issue() shortcut_handler = new ShortcutsIssuable() new ZenMode() + awards_handler = new AwardsHandler() when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show' new Milestone() when 'dashboard:todos:index' @@ -52,6 +53,7 @@ class Dispatcher new Diff() shortcut_handler = new ShortcutsIssuable(true) new ZenMode() + awards_handler = new AwardsHandler() when "projects:merge_requests:diffs" new Diff() new ZenMode() diff --git a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb b/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb new file mode 100644 index 00000000000..e005ab3684e --- /dev/null +++ b/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb @@ -0,0 +1,9 @@ +((w) -> + + w.gl ?= {} + w.gl.emoji ?= {} + + w.gl.emoji.emojiAliases = -> + JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>') + +) window
\ No newline at end of file diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index fa91baa07c0..ae8c1f22e4c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -150,12 +150,12 @@ class @Notes renderNote: (note) -> unless note.valid if note.award - flash = new Flash('You have already used this award emoji!', 'alert') + flash = new Flash('You have already awarded this emoji, and it we\'ve removed it', 'alert') flash.pinTo('.header-content') return if note.award - awards_handler.addAwardToEmojiBar(note.note) + awards_handler.addAwardToEmojiBar(note.name) awards_handler.scrollToAwards() # render note if it not present in loaded list diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss index 37bf38fa65d..07d40f40556 100644 --- a/app/assets/stylesheets/pages/awards.scss +++ b/app/assets/stylesheets/pages/awards.scss @@ -1,6 +1,4 @@ .awards { - line-height: 34px; - .emoji-icon { width: 20px; height: 20px; @@ -9,8 +7,6 @@ .emoji-menu { position: absolute; - top: 100%; - left: 0; margin-top: 3px; z-index: 1000; min-width: 160px; @@ -23,7 +19,12 @@ opacity: 0; transform: scale(.2); transform-origin: 0 -45px; - transition: all .3s cubic-bezier(.87,-.41,.19,1.44); + transition: .3s cubic-bezier(.87,-.41,.19,1.44); + transition-property: transform, opacity; + + &.is-aligned-right { + transform-origin: 100% -45px; + } &.is-visible { pointer-events: all; @@ -107,7 +108,7 @@ } &.is-loading { - .award-control-icon { + .award-control-icon-normal { display: none; } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index ce44f5aa13b..9d808cce152 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -63,7 +63,8 @@ ul.notes { &.is-editting { .note-header, .note-text, - .edited-text { + .edited-text, + .note-awards { display: none; } @@ -73,8 +74,6 @@ ul.notes { } .note-body { - overflow: auto; - .note-text { overflow: auto; word-wrap: break-word; @@ -307,6 +306,42 @@ ul.notes { } } +.note-award-control { + display: block; + + &:hover, + &:focus { + text-decoration: none; + } + + .award-control-icon-loading { + display: none; + } + + &.is-loading { + .award-control-icon-normal { + display: none; + } + + .award-control-icon-loading { + display: block; + } + } +} + +.note-awards { + .awards { + padding-top: 10px; + } + + .award-control { + padding-top: 2px; + padding-bottom: 2px; + color: #8f8f8f; + font-size: 13px; + } +} + .disabled-comment { margin-left: -$gl-padding-top; margin-right: -$gl-padding-top; diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb index fa4c635f55c..ab252821b52 100644 --- a/app/finders/notes_finder.rb +++ b/app/finders/notes_finder.rb @@ -12,9 +12,9 @@ class NotesFinder when "commit" project.notes.for_commit_id(target_id).not_inline when "issue" - project.issues.find(target_id).notes.nonawards.inc_author + project.issues.find(target_id).notes.inc_author when "merge_request" - project.merge_requests.find(target_id).mr_and_commit_notes.nonawards.inc_author + project.merge_requests.find(target_id).mr_and_commit_notes.inc_author when "snippet", "project_snippet" project.snippets.find(target_id).notes else diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml index 63c953195fe..b57c9afcbd2 100644 --- a/app/views/award_emoji/_awards_block.html.haml +++ b/app/views/award_emoji/_awards_block.html.haml @@ -1,7 +1,7 @@ -- grouped_awards = awardable.grouped_awards(inline) +- grouped_emojis = 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" } } + - awards_sort(grouped_emojis).each do |emoji, awards| + %button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button", class: (award_active_class(awards, current_user)),data: { placement: "bottom", original_title: award_user_list(awards, current_user)} } = emoji_icon(emoji) %span.award-control-text.js-counter = awards.count |