diff options
author | Bob Van Landuyt <bob@vanlanduyt.co> | 2018-07-16 19:18:52 +0300 |
---|---|---|
committer | Bob Van Landuyt <bob@vanlanduyt.co> | 2018-07-30 16:01:26 +0300 |
commit | f1d3ea63cf74d2791a9a863b29ab2d919ea61bd0 (patch) | |
tree | c36c2b272fba917af321a4f16c34a5047407f3b2 /app | |
parent | b4c4b48a8c0258ff266c523488aa169a1b5ea0f3 (diff) |
Show the status of a user in interactions
The status is shown for
- The author of a commit when viewing a commit
- Notes on a commit (regular/diff)
- The user that triggered a pipeline when viewing a pipeline
- The author of a merge request when viewing a merge request
- The author of notes on a merge request (regular/diff)
- The author of an issue when viewing an issue
- The author of notes on an issue
- The author of a snippet when viewing a snippet
- The author of notes on a snippet
- A user's profile page
- The list of members of a group/user
Diffstat (limited to 'app')
28 files changed, 105 insertions, 14 deletions
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue index ee3580895df..a621418cf72 100644 --- a/app/assets/javascripts/notes/components/note_header.vue +++ b/app/assets/javascripts/notes/components/note_header.vue @@ -74,6 +74,9 @@ export default { </div> <a :href="author.path"> <span class="note-header-author-name">{{ author.name }}</span> + <span + v-if="author.status_tooltip_html" + v-html="author.status_tooltip_html"></span> <span class="note-headline-light"> @{{ author.username }} </span> diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index 62d35f6547d..49fbce75110 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -113,6 +113,9 @@ export default { {{ user.name }} </a> + <span + v-if="user.status_tooltip_html" + v-html="user.status_tooltip_html"></span> </template> </section> diff --git a/app/controllers/concerns/members_presentation.rb b/app/controllers/concerns/members_presentation.rb index c0622516fd3..215e0bdf3cb 100644 --- a/app/controllers/concerns/members_presentation.rb +++ b/app/controllers/concerns/members_presentation.rb @@ -2,10 +2,18 @@ module MembersPresentation extend ActiveSupport::Concern def present_members(members) + preload_associations(members) Gitlab::View::Presenter::Factory.new( members, current_user: current_user, presenter_class: MembersPresenter ).fabricate! end + + def preload_associations(members) + ActiveRecord::Associations::Preloader.new.preload(members, :user) + ActiveRecord::Associations::Preloader.new.preload(members, :source) + ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :status) + ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :u2f_registrations) + end end diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb index 7a6a00b8e13..409e6d4c4d2 100644 --- a/app/controllers/concerns/membership_actions.rb +++ b/app/controllers/concerns/membership_actions.rb @@ -1,4 +1,5 @@ module MembershipActions + include MembersPresentation extend ActiveSupport::Concern def create @@ -20,6 +21,7 @@ module MembershipActions .execute(member) .present(current_user: current_user) + present_members([member]) respond_to do |format| format.js { render 'shared/members/update', locals: { member: member } } end diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb index fe9a030cdf2..5127db3f5fb 100644 --- a/app/controllers/concerns/notes_actions.rb +++ b/app/controllers/concerns/notes_actions.rb @@ -41,7 +41,7 @@ module NotesActions @note = Notes::CreateService.new(note_project, current_user, create_params).execute if @note.is_a?(Note) - Notes::RenderService.new(current_user).execute([@note]) + prepare_notes_for_rendering([@note], noteable) end respond_to do |format| @@ -56,7 +56,7 @@ module NotesActions @note = Notes::UpdateService.new(project, current_user, note_params).execute(note) if @note.is_a?(Note) - Notes::RenderService.new(current_user).execute([@note]) + prepare_notes_for_rendering([@note]) end respond_to do |format| diff --git a/app/controllers/concerns/renders_notes.rb b/app/controllers/concerns/renders_notes.rb index 36e3d76ecfe..cf04023080a 100644 --- a/app/controllers/concerns/renders_notes.rb +++ b/app/controllers/concerns/renders_notes.rb @@ -4,6 +4,7 @@ module RendersNotes preload_noteable_for_regular_notes(notes) preload_max_access_for_authors(notes, @project) preload_first_time_contribution_for_authors(noteable, notes) + preload_author_status(notes) Notes::RenderService.new(current_user).execute(notes) notes @@ -28,4 +29,8 @@ module RendersNotes notes.each {|n| n.specialize_for_first_contribution!(noteable)} end + + def preload_author_status(notes) + ActiveRecord::Associations::Preloader.new.preload(notes, { author: :status }) + end end diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index ef5d5e5c742..7dc51f4c357 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -30,7 +30,7 @@ class Groups::GroupMembersController < Groups::ApplicationController end @members = @members.page(params[:page]).per(50) - @members = present_members(@members.includes(:user)) + @members = present_members(@members) @requesters = present_members( AccessRequestsFinder.new(@group).execute(current_user)) diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 1d1184d46d1..44b176d304e 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -22,7 +22,9 @@ class Projects::CommitController < Projects::ApplicationController apply_diff_view_cookie! respond_to do |format| - format.html { render } + format.html do + render + end format.diff do send_git_diff(@project.repository, @commit.diff_refs) end @@ -124,7 +126,10 @@ class Projects::CommitController < Projects::ApplicationController end def commit - @noteable = @commit ||= @project.commit_by(oid: params[:id]) + @noteable = @commit ||= @project.commit_by(oid: params[:id]).tap do |commit| + # preload author and their status for rendering + commit&.author&.status + end end def define_commit_vars diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 7c897b2d86c..ef8159aa553 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -165,7 +165,7 @@ class Projects::IssuesController < Projects::ApplicationController return @issue if defined?(@issue) # The Sortable default scope causes performance issues when used with find_by - @issuable = @noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take! + @issuable = @noteable = @issue ||= @project.issues.includes(author: :status).where(iid: params[:id]).reorder(nil).take! @note = @project.notes.new(noteable: @issuable) return render_404 unless can?(current_user, :read_issue, @issue) diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb index 8e4aeec16dc..fead81dd472 100644 --- a/app/controllers/projects/merge_requests/application_controller.rb +++ b/app/controllers/projects/merge_requests/application_controller.rb @@ -6,7 +6,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont private def merge_request - @issuable = @merge_request ||= @project.merge_requests.find_by!(iid: params[:id]) + @issuable = @merge_request ||= @project.merge_requests.includes(author: :status).find_by!(iid: params[:id]) end def merge_request_params diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 4d4c2af2415..21e2145b73b 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -1,4 +1,5 @@ class Projects::NotesController < Projects::ApplicationController + include RendersNotes include NotesActions include NotesHelper include ToggleAwardEmoji @@ -53,7 +54,7 @@ class Projects::NotesController < Projects::ApplicationController private def render_json_with_notes_serializer - Notes::RenderService.new(current_user).execute([note]) + prepare_notes_for_rendering([note]) render json: note_serializer.represent(note) end diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 45cef123c34..1d5d1ee5d5a 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -161,7 +161,11 @@ class Projects::PipelinesController < Projects::ApplicationController end def pipeline - @pipeline ||= project.pipelines.find_by!(id: params[:id]).present(current_user: current_user) + @pipeline ||= project + .pipelines + .includes(user: :status) + .find_by!(id: params[:id]) + .present(current_user: current_user) end def commit diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index f742d7edf83..7c03d8ce827 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -88,7 +88,7 @@ class Projects::SnippetsController < Projects::ApplicationController protected def snippet - @snippet ||= @project.snippets.find(params[:id]) + @snippet ||= @project.snippets.inc_relations_for_view.find(params[:id]) end alias_method :awardable, :snippet alias_method :spammable, :snippet diff --git a/app/controllers/snippets/notes_controller.rb b/app/controllers/snippets/notes_controller.rb index c8b4682e6dc..217da89a1fd 100644 --- a/app/controllers/snippets/notes_controller.rb +++ b/app/controllers/snippets/notes_controller.rb @@ -9,7 +9,7 @@ class Snippets::NotesController < ApplicationController private def note - @note ||= snippet.notes.find(params[:id]) + @note ||= snippet.notes.inc_relations_for_view.find(params[:id]) end alias_method :awardable, :note diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 1d6d0943674..dcf18c1f751 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -95,7 +95,7 @@ class SnippetsController < ApplicationController protected def snippet - @snippet ||= PersonalSnippet.find_by(id: params[:id]) + @snippet ||= PersonalSnippet.inc_relations_for_view.find_by(id: params[:id]) end alias_method :awardable, :snippet diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 8766bb43cac..678fed9c414 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -159,6 +159,12 @@ module IssuablesHelper output << content_tag(:strong) do author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "d-none d-sm-inline", tooltip: true) author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "d-block d-sm-none") + + if status = user_status(issuable.author) + author_output << "  #{status}".html_safe + end + + author_output end output << " ".html_safe diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 8ee4203b6f5..ceea4384f91 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -39,6 +39,24 @@ module UsersHelper "access:#{max_project_member_access(project)}" end + def user_status(user) + return unless user + + unless user.association(:status).loaded? + exception = RuntimeError.new("Status was not preloaded") + Gitlab::Sentry.track_exception(exception, extra: { user: user.inspect }) + end + + return unless user.status + + content_tag :span, + class: 'user-status-emoji has-tooltip', + title: user.status.message_html, + data: { html: true, placement: 'top' } do + emoji_icon user.status.emoji + end + end + private def get_profile_tabs diff --git a/app/models/note.rb b/app/models/note.rb index fe3507adcb3..87cf7c7b2fa 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -101,7 +101,7 @@ class Note < ActiveRecord::Base scope :inc_author_project, -> { includes(:project, :author) } scope :inc_author, -> { includes(:author) } scope :inc_relations_for_view, -> do - includes(:project, :author, :updated_by, :resolved_by, :award_emoji, + includes(:project, { author: :status }, :updated_by, :resolved_by, :award_emoji, :system_note_metadata, :note_diff_file) end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 644120453cf..390bdbf838a 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -49,6 +49,7 @@ class Snippet < ActiveRecord::Base scope :are_public, -> { where(visibility_level: Snippet::PUBLIC) } scope :public_and_internal, -> { where(visibility_level: [Snippet::PUBLIC, Snippet::INTERNAL]) } scope :fresh, -> { order("created_at DESC") } + scope :inc_relations_for_view, -> { includes(author: :status) } participant :author participant :notes_with_associations diff --git a/app/models/user_status.rb b/app/models/user_status.rb index 72938cfbca0..2bbb0c59ac1 100644 --- a/app/models/user_status.rb +++ b/app/models/user_status.rb @@ -3,6 +3,10 @@ class UserStatus < ActiveRecord::Base include CacheMarkdownField + self.primary_key = :user_id + + DEFAULT_EMOJI = 'speech_balloon'.freeze + belongs_to :user validates :user, presence: true diff --git a/app/serializers/concerns/user_status_tooltip.rb b/app/serializers/concerns/user_status_tooltip.rb new file mode 100644 index 00000000000..aa6e67e3351 --- /dev/null +++ b/app/serializers/concerns/user_status_tooltip.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module UserStatusTooltip + extend ActiveSupport::Concern + include ActionView::Helpers::TagHelper + include ActionView::Context + include EmojiHelper + include UsersHelper + + included do + expose :user_status_if_loaded, as: :status_tooltip_html + + def user_status_if_loaded + return nil unless object.association(:status).loaded? + + user_status(object) + end + end +end diff --git a/app/serializers/user_entity.rb b/app/serializers/user_entity.rb index 6236d66ff4a..656900bb8af 100644 --- a/app/serializers/user_entity.rb +++ b/app/serializers/user_entity.rb @@ -2,6 +2,7 @@ class UserEntity < API::Entities::UserBasic include RequestAwareEntity + include UserStatusTooltip expose :path do |user| user_path(user) diff --git a/app/services/users/set_status_service.rb b/app/services/users/set_status_service.rb index 275e13faf59..89008368d5f 100644 --- a/app/services/users/set_status_service.rb +++ b/app/services/users/set_status_service.rb @@ -24,6 +24,7 @@ module Users private def set_status + params[:emoji] = UserStatus::DEFAULT_EMOJI if params[:emoji].blank? user_status.update(params) end diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 78522393d4b..8afbbaf378f 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -13,6 +13,7 @@ = author_avatar(@commit, size: 24, has_tooltip: false) %strong = commit_author_link(@commit, avatar: true, size: 24) + = user_status(@commit.author) - if @commit.different_committer? %span.light= _('Committed by') %strong diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 46debe1f2b9..af29c0fe59e 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -11,6 +11,7 @@ = image_tag avatar_icon_for_user(user, 40), class: "avatar s40", alt: '' .user-info = link_to user.name, user_path(user), class: 'member' + = user_status(user) %span.cgray= user.to_reference - if user == current_user diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index f5464058bc0..84adbd444c5 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -31,7 +31,9 @@ .note-header .note-header-info %a{ href: user_path(note.author) } - %span.note-header-author-name= sanitize(note.author.name) + %span.note-header-author-name + = sanitize(note.author.name) + = user_status(note.author) %span.note-headline-light = note.author.to_reference %span.note-headline-light diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 828ec870dc0..10bfc30492a 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -8,6 +8,7 @@ Authored = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title", avatar_class: "d-none d-sm-inline")} + = user_status(@snippet.author) .detail-page-header-actions - if @snippet.project_id? diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 8d9e86d02c4..7a38d290915 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -40,6 +40,11 @@ .cover-title = @user.name + - if @user.status + .cover-status + = emoji_icon(@user.status.emoji) + = markdown_field(@user.status, :message) + .cover-desc.member-date %p %span.middle-dot-divider |