diff options
-rw-r--r-- | CHANGELOG | 1 | ||||
-rw-r--r-- | app/assets/javascripts/gfm_auto_complete.js.coffee | 63 | ||||
-rw-r--r-- | app/services/projects/participants_service.rb | 43 | ||||
-rw-r--r-- | spec/features/participants_autocomplete_spec.rb | 98 |
4 files changed, 172 insertions, 33 deletions
diff --git a/CHANGELOG b/CHANGELOG index f1320b6a28c..5c375fcdb39 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -74,6 +74,7 @@ v 8.7.0 (unreleased) - Selected diff rows highlight - Fix emoji categories in the emoji picker - Add encrypted credentials for imported projects and migrate old ones + - Author and participants are displayed first on users autocompletion v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee index 4718bcf7a1e..61e3f811e73 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.coffee +++ b/app/assets/javascripts/gfm_auto_complete.js.coffee @@ -2,6 +2,8 @@ window.GitLab ?= {} GitLab.GfmAutoComplete = + dataLoading: false + dataSource: '' # Emoji @@ -17,17 +19,41 @@ GitLab.GfmAutoComplete = template: '<li><small>${id}</small> ${title}</li>' # Add GFM auto-completion to all input fields, that accept GFM input. - setup: -> - input = $('.js-gfm-input') + setup: (wrap) -> + @input = $('.js-gfm-input') + + # destroy previous instances + @destroyAtWho() + + # set up instances + @setupAtWho() + + if @dataSource + if !@dataLoading + @dataLoading = true + # We should wait until initializations are done + # and only trigger the last .setup since + # The previous .dataSource belongs to the previous issuable + # and the last one will have the **proper** .dataSource property + # TODO: Make this a singleton and turn off events when moving to another page + setTimeout( => + fetch = @fetchData(@dataSource) + fetch.done (data) => + @dataLoading = false + @loadData(data) + , 1000) + + + setupAtWho: -> # Emoji - input.atwho + @input.atwho at: ':' displayTpl: @Emoji.template insertTpl: ':${name}:' # Team Members - input.atwho + @input.atwho at: '@' displayTpl: @Members.template insertTpl: '${atwho-at}${username}' @@ -42,7 +68,7 @@ GitLab.GfmAutoComplete = title: sanitize(title) search: sanitize("#{m.username} #{m.name}") - input.atwho + @input.atwho at: '#' alias: 'issues' searchKey: 'search' @@ -55,7 +81,7 @@ GitLab.GfmAutoComplete = title: sanitize(i.title) search: "#{i.iid} #{i.title}" - input.atwho + @input.atwho at: '!' alias: 'mergerequests' searchKey: 'search' @@ -68,13 +94,18 @@ GitLab.GfmAutoComplete = title: sanitize(m.title) search: "#{m.iid} #{m.title}" - if @dataSource - $.getJSON(@dataSource).done (data) -> - # load members - input.atwho 'load', '@', data.members - # load issues - input.atwho 'load', 'issues', data.issues - # load merge requests - input.atwho 'load', 'mergerequests', data.mergerequests - # load emojis - input.atwho 'load', ':', data.emojis + destroyAtWho: -> + @input.atwho('destroy') + + fetchData: (dataSource) -> + $.getJSON(dataSource) + + loadData: (data) -> + # load members + @input.atwho 'load', '@', data.members + # load issues + @input.atwho 'load', 'issues', data.issues + # load merge requests + @input.atwho 'load', 'mergerequests', data.mergerequests + # load emojis + @input.atwho 'load', ':', data.emojis diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index 0004a399f47..02c4eee3d02 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -1,28 +1,37 @@ module Projects class ParticipantsService < BaseService - def execute(note_type, note_id) - participating = - if note_type && note_id - participants_in(note_type, note_id) - else - [] - end + def execute(noteable_type, noteable_id) + @noteable_type = noteable_type + @noteable_id = noteable_id project_members = sorted(project.team.members) - participants = all_members + groups + project_members + participating + participants = target_owner + participants_in_target + all_members + groups + project_members participants.uniq end - def participants_in(type, id) - target = - case type + def target + @target ||= + case @noteable_type when "Issue" - project.issues.find_by_iid(id) + project.issues.find_by_iid(@noteable_id) when "MergeRequest" - project.merge_requests.find_by_iid(id) + project.merge_requests.find_by_iid(@noteable_id) when "Commit" - project.commit(id) + project.commit(@noteable_id) + else + nil end - + end + + def target_owner + return [] unless target && target.author.present? + + [{ + name: target.author.name, + username: target.author.username + }] + end + + def participants_in_target return [] unless target users = target.participants(current_user) @@ -30,13 +39,13 @@ module Projects end def sorted(users) - users.uniq.to_a.compact.sort_by(&:username).map do |user| + users.uniq.to_a.compact.sort_by(&:username).map do |user| { username: user.username, name: user.name } end end def groups - current_user.authorized_groups.sort_by(&:path).map do |group| + current_user.authorized_groups.sort_by(&:path).map do |group| count = group.users.count { username: group.path, name: group.name, count: count } end diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb new file mode 100644 index 00000000000..1adab7e9c6c --- /dev/null +++ b/spec/features/participants_autocomplete_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' + +feature 'Member autocomplete', feature: true do + let(:project) { create(:project, :public) } + let(:user) { create(:user) } + let(:participant) { create(:user) } + let(:author) { create(:user) } + + before do + allow_any_instance_of(Commit).to receive(:author).and_return(author) + login_as user + end + + shared_examples "open suggestions" do + it 'suggestions are displayed' do + expect(page).to have_selector('.atwho-view', visible: true) + end + + it 'author is suggested' do + page.within('.atwho-view', visible: true) do + expect(page).to have_content(author.username) + end + end + + it 'participant is suggested' do + page.within('.atwho-view', visible: true) do + expect(page).to have_content(participant.username) + end + end + end + + context 'adding a new note on a Issue', js: true do + before do + issue = create(:issue, author: author, project: project) + create(:note, note: 'Ultralight Beam', noteable: issue, author: participant) + visit_issue(project, issue) + end + + context 'when typing @' do + include_examples "open suggestions" + before do + open_member_suggestions + end + end + end + + context 'adding a new note on a Merge Request ', js: true do + before do + merge = create(:merge_request, source_project: project, target_project: project, author: author) + create(:note, note: 'Ultralight Beam', noteable: merge, author: participant) + visit_merge_request(project, merge) + end + + context 'when typing @' do + include_examples "open suggestions" + before do + open_member_suggestions + end + end + end + + context 'adding a new note on a Commit ', js: true do + let(:commit) { project.commit } + + before do + allow(commit).to receive(:author).and_return(author) + create(:note_on_commit, author: participant, project: project, commit_id: project.repository.commit.id, note: 'No More Parties in LA') + visit_commit(project, commit) + end + + context 'when typing @' do + include_examples "open suggestions" + before do + open_member_suggestions + end + end + end + + def open_member_suggestions + sleep 1 + page.within('.new-note') do + sleep 1 + find('#note_note').native.send_keys('@') + end + end + + def visit_issue(project, issue) + visit namespace_project_issue_path(project.namespace, project, issue) + end + + def visit_merge_request(project, merge) + visit namespace_project_merge_request_path(project.namespace, project, merge) + end + + def visit_commit(project, commit) + visit namespace_project_commit_path(project.namespace, project, commit) + end +end |