gitlab-ce/app/assets/javascripts/merge_request_dashboard/components/assigned_users.vue

175 lines
5.1 KiB
Vue

<script>
import { GlIcon, GlAvatarsInline, GlAvatarLink, GlAvatar, GlTooltipDirective } from '@gitlab/ui';
import { __, n__, sprintf } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { swapArrayItems } from '~/lib/utils/array_utility';
import { isCurrentUser } from '~/lib/utils/common_utils';
const MAX_VISIBLE_USERS = 3;
const REVIEW_STATE_ICONS = {
APPROVED: {
name: 'check-circle',
class: 'gl-bg-green-100 gl-text-green-500',
},
REQUESTED_CHANGES: {
name: 'error',
class: 'gl-bg-red-100 gl-text-red-500',
},
REVIEWED: {
name: 'comment-lines',
class: 'gl-bg-blue-100 gl-text-blue-500',
},
REVIEW_STARTED: {
name: 'comment-dots',
class: 'gl-bg-gray-100 gl-text-gray-500',
},
};
const USER_TOOLTIP_TITLES = {
ASSIGNEES: __('%{strongStart}%{you}%{strongEnd}%{break}Assigned to %{name}'),
REQUESTED_CHANGES: __('%{strongStart}%{you}%{strongEnd}%{break}%{name} requested changes'),
REVIEWED: __('%{strongStart}%{you}%{strongEnd}%{break}%{name} left feedback'),
APPROVED: __('%{strongStart}%{you}%{strongEnd}%{break}Approved by %{name}'),
UNREVIEWED: __('%{strongStart}%{you}%{strongEnd}%{break}Review requested from %{name}'),
REVIEW_STARTED: __('%{strongStart}%{you}%{strongEnd}%{break}%{name} started a review'),
default: __('%{strongStart}%{you}%{strongEnd}%{break}Review requested from %{name}'),
};
export default {
components: {
GlIcon,
GlAvatarsInline,
GlAvatarLink,
GlAvatar,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
users: {
type: Array,
required: true,
},
type: {
type: String,
required: true,
},
},
computed: {
usersBadgeSrOnlyText() {
if (this.type === 'ASSIGNEES') {
return n__(
'%d additional assignee',
'%d additional assignees',
this.users.length - MAX_VISIBLE_USERS,
);
}
return n__(
'%d additional reviewer',
'%d additional reviewers',
this.users.length - MAX_VISIBLE_USERS,
);
},
sortedUsers() {
const currentUserIndex = this.users.findIndex((u) => this.isCurrentUser(u));
if (currentUserIndex === -1) return this.users;
const addIndex = Math.min(Math.max(this.users.length - 1, 0), 2);
return swapArrayItems(this.users, currentUserIndex, addIndex);
},
},
methods: {
isCurrentUser(user) {
return isCurrentUser(getIdFromGraphQLId(user.id));
},
reviewStateIcon(user) {
return REVIEW_STATE_ICONS[user.mergeRequestInteraction?.reviewState];
},
tooltipTitle(user) {
const currentUser = this.isCurrentUser(user);
const thisIsYouText = currentUser ? __('This is you.') : '';
let titleType;
if (this.type === 'ASSIGNEES') {
titleType = this.type;
} else if (user.mergeRequestInteraction?.reviewState) {
titleType = user.mergeRequestInteraction.reviewState;
}
return sprintf(
USER_TOOLTIP_TITLES[titleType] || USER_TOOLTIP_TITLES.default,
{
name: user.name,
you: thisIsYouText,
break: currentUser ? '<br />' : '',
strongStart: currentUser ? '<strong>' : '',
strongEnd: currentUser ? '</strong>' : '',
},
false,
);
},
},
MAX_VISIBLE_USERS,
REVIEW_STATE_ICONS,
};
</script>
<template>
<div class="mr-users-list gl-flex gl-justify-center">
<gl-avatars-inline
v-if="sortedUsers.length"
:avatars="sortedUsers"
collapsed
:avatar-size="32"
:max-visible="$options.MAX_VISIBLE_USERS"
:badge-sr-only-text="usersBadgeSrOnlyText"
badge-tooltip-prop="name"
>
<template #avatar="{ avatar: user }">
<gl-avatar-link
v-gl-tooltip.viewport.top.html="tooltipTitle(user)"
target="blank"
:href="user.webUrl"
class="gl-relative"
data-testid="assigned-user"
>
<gl-avatar :src="user.avatarUrl" :size="32" />
<span
v-if="isCurrentUser(user)"
class="gl-absolute -gl-left-2 -gl-top-2 gl-flex gl-h-5 gl-w-5 gl-items-center gl-justify-center gl-rounded-full gl-bg-blue-500 gl-p-1 gl-text-white"
data-testid="current-user"
>
<gl-icon name="user" class="gl-block" :size="12" />
</span>
<span
v-if="reviewStateIcon(user)"
class="gl-absolute -gl-bottom-2 -gl-right-2 gl-flex gl-h-5 gl-w-5 gl-items-center gl-justify-center gl-rounded-full gl-p-1"
:class="reviewStateIcon(user).class"
data-testid="review-state-icon"
>
<gl-icon :name="reviewStateIcon(user).name" class="gl-block" :size="12" />
</span>
</gl-avatar-link>
</template>
</gl-avatars-inline>
<gl-icon v-else name="dash" />
</div>
</template>
<style>
.mr-users-list .gl-avatars-inline-child,
.mr-users-list .gl-avatars-inline-badge,
.mr-users-list .gl-avatar-link {
-webkit-mask: none !important;
mask: none !important;
}
.mr-users-list .gl-avatar {
outline: 2px solid var(--white);
outline-offset: 0;
}
</style>