app/assets/javascripts/todos/components/todo_item_body.vue (188 lines of code) (raw):

<script> import { GlLink, GlAvatar, GlAvatarLink } from '@gitlab/ui'; import SafeHtml from '~/vue_shared/directives/safe_html'; import { s__, sprintf } from '~/locale'; import * as Sentry from '~/sentry/sentry_browser_wrapper'; import { TODO_ACTION_TYPE_ADDED_APPROVER, TODO_ACTION_TYPE_APPROVAL_REQUIRED, TODO_ACTION_TYPE_ASSIGNED, TODO_ACTION_TYPE_BUILD_FAILED, TODO_ACTION_TYPE_DIRECTLY_ADDRESSED, TODO_ACTION_TYPE_MARKED, TODO_ACTION_TYPE_MEMBER_ACCESS_REQUESTED, TODO_ACTION_TYPE_MENTIONED, TODO_ACTION_TYPE_MERGE_TRAIN_REMOVED, TODO_ACTION_TYPE_OKR_CHECKIN_REQUESTED, TODO_ACTION_TYPE_REVIEW_REQUESTED, TODO_ACTION_TYPE_REVIEW_SUBMITTED, TODO_ACTION_TYPE_UNMERGEABLE, TODO_ACTION_TYPE_SSH_KEY_EXPIRED, TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON, DUO_ACCESS_GRANTED_ACTIONS, } from '../constants'; export default { components: { GlLink, GlAvatar, GlAvatarLink, }, directives: { SafeHtml, }, props: { currentUserId: { type: String, required: true, }, todo: { type: Object, required: true, }, isHiddenBySaml: { type: Boolean, required: false, default: false, }, }, computed: { noteText() { if (!this.todo.note) { return null; } return this.todo.note.bodyFirstLineHtml.replace(/(<\/?)p>/g, '$1span>'); }, showAuthorOnNote() { return ( this.todo.action !== TODO_ACTION_TYPE_BUILD_FAILED && this.todo.action !== TODO_ACTION_TYPE_MERGE_TRAIN_REMOVED && this.todo.action !== TODO_ACTION_TYPE_UNMERGEABLE && this.todo.action !== TODO_ACTION_TYPE_SSH_KEY_EXPIRED && this.todo.action !== TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON && !DUO_ACCESS_GRANTED_ACTIONS.includes(this.todo.action) ); }, showAvatarOnNote() { return !DUO_ACCESS_GRANTED_ACTIONS.includes(this.todo.action); }, author() { if (this.isHiddenBySaml) { return { name: s__('Todos|Someone'), webUrl: this.todo.targetUrl, avatarUrl: gon.default_avatar_url, }; } return this.todo.author; }, userIsAuthor() { return this.author.id === this.currentUserId; }, authorOnNote() { return this.userIsAuthor ? s__('Todos|You') : this.author.name; }, actionSubject() { return this.userIsAuthor && !this.isHiddenBySaml ? s__('Todos|yourself') : s__('Todos|you'); }, actionName() { if (this.todo.note) { return null; } let name = ''; if (this.todo.action === TODO_ACTION_TYPE_ASSIGNED) { name = this.userIsAuthor ? s__('Todos|assigned to yourself') : s__('Todos|assigned you'); } if (this.todo.action === TODO_ACTION_TYPE_REVIEW_REQUESTED) { name = this.userIsAuthor ? s__('Todos|requested a review from yourself') : s__('Todos|requested a review'); } if ( this.todo.action === TODO_ACTION_TYPE_MENTIONED || this.todo.action === TODO_ACTION_TYPE_DIRECTLY_ADDRESSED ) { name = sprintf(s__('Todos|mentioned %{who}'), { who: this.actionSubject }); } if (this.todo.action === TODO_ACTION_TYPE_BUILD_FAILED) { name = s__('Todos|The pipeline failed'); } if (this.todo.action === TODO_ACTION_TYPE_MARKED) { name = s__('Todos|added a to-do item'); } if ( this.todo.action === TODO_ACTION_TYPE_APPROVAL_REQUIRED || this.todo.action === TODO_ACTION_TYPE_ADDED_APPROVER ) { name = s__('Todos|created a merge request you can approve'); } if (this.todo.action === TODO_ACTION_TYPE_UNMERGEABLE) { name = s__('Todos|Could not merge'); } if (this.todo.action === TODO_ACTION_TYPE_MERGE_TRAIN_REMOVED) { name = s__('Todos|Removed from Merge Train'); } if (this.todo.action === TODO_ACTION_TYPE_MEMBER_ACCESS_REQUESTED) { name = sprintf(s__('Todos|has requested access to %{what} %{which}'), { what: this.todo.memberAccessType, which: this.todo.targetEntity.name, }); } if (this.todo.action === TODO_ACTION_TYPE_REVIEW_SUBMITTED) { name = s__('Todos|reviewed your merge request'); } if (this.todo.action === TODO_ACTION_TYPE_OKR_CHECKIN_REQUESTED) { name = sprintf(s__('Todos|requested an OKR update for %{which}'), { which: this.todo.targetEntity.name, }); } if (this.todo.action === TODO_ACTION_TYPE_SSH_KEY_EXPIRED) { name = s__('Todos|Your SSH key has expired'); } if (this.todo.action === TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON) { name = s__('Todos|Your SSH key is expiring soon'); } if (DUO_ACCESS_GRANTED_ACTIONS.includes(this.todo.action)) { name = s__( 'Todos|You now have access to AI-native features. Learn how to set up Code Suggestions and Chat in your IDE', ); } if (!name) { Sentry.captureException( new Error(`Encountered unknown TODO_ACTION_TYPE ${this.todo.action}`), ); return null; } return `${name}.`; }, }, i18n: { removed: s__('Todos|(removed)'), }, }; </script> <template> <div class="gl-flex gl-items-start gl-px-2" data-testid="todo-item-container"> <div v-if="showAvatarOnNote" class="gl-mr-3 gl-hidden sm:gl-inline-block"> <gl-avatar-link :href="author.webUrl" aria-hidden="true" tabindex="-1"> <gl-avatar :size="24" :src="author.avatarUrl" role="none" /> </gl-avatar-link> </div> <div> <div v-if="showAuthorOnNote" class="gl-inline-flex gl-font-bold"> <gl-link v-if="author" :href="author.webUrl" class="!gl-text-default" data-testid="todo-author-name-content" >{{ authorOnNote }}</gl-link > <span v-else>{{ $options.i18n.removed }}</span> <span v-if="todo.note">:</span> </div> <span v-if="actionName" data-testid="todo-action-name-content"> {{ actionName }} </span> <span v-if="noteText" v-safe-html="noteText"></span> <!-- TODO: AI? Review summary here: https://gitlab.com/gitlab-org/gitlab/-/work_items/483061 --> </div> </div> </template>