diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
commit | 85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch) | |
tree | 9160f299afd8c80c038f08e1545be119f5e3f1e1 /app/assets/javascripts/notes | |
parent | 15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff) | |
download | gitlab-ce-85dc423f7090da0a52c73eb66faf22ddb20efff9.tar.gz |
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'app/assets/javascripts/notes')
23 files changed, 186 insertions, 96 deletions
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 7cfff98e9f7..54fcf41ca50 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -3,7 +3,7 @@ import $ from 'jquery'; import { mapActions, mapGetters, mapState } from 'vuex'; import { isEmpty } from 'lodash'; import Autosize from 'autosize'; -import { GlAlert, GlIntersperse, GlLink, GlSprintf } from '@gitlab/ui'; +import { GlAlert, GlIntersperse, GlLink, GlSprintf, GlButton } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; import { deprecatedCreateFlash as Flash } from '../../flash'; @@ -20,7 +20,6 @@ import eventHub from '../event_hub'; import NoteableWarning from '../../vue_shared/components/notes/noteable_warning.vue'; import markdownField from '../../vue_shared/components/markdown/field.vue'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; -import loadingButton from '../../vue_shared/components/loading_button.vue'; import noteSignedOutWidget from './note_signed_out_widget.vue'; import discussionLockedWidget from './discussion_locked_widget.vue'; import issuableStateMixin from '../mixins/issuable_state'; @@ -33,7 +32,7 @@ export default { discussionLockedWidget, markdownField, userAvatarLink, - loadingButton, + GlButton, TimelineEntryItem, GlAlert, GlIntersperse, @@ -102,6 +101,9 @@ export default { noteable: this.noteableDisplayName, }); }, + buttonVariant() { + return this.isOpen ? 'warning' : 'default'; + }, actionButtonClassNames() { return { 'btn-reopen': !this.isOpen, @@ -378,7 +380,7 @@ export default { dir="auto" :disabled="isSubmitting" name="note[note]" - class="note-textarea js-vue-comment-form js-note-text js-gfm-input js-autosize markdown-area js-vue-textarea qa-comment-input" + class="note-textarea js-vue-comment-form js-note-text js-gfm-input js-autosize markdown-area qa-comment-input" data-supports-quick-actions="true" :aria-label="__('Description')" :placeholder="__('Write a comment or drag your files here…')" @@ -395,7 +397,7 @@ export default { :secondary-button-text="__('Cancel')" variant="warning" :dismissible="false" - @primaryAction="forceCloseIssue" + @primaryAction="toggleBlockedIssueWarning(false) && forceCloseIssue()" @secondaryAction="toggleBlockedIssueWarning(false) && enableButton()" > <p> @@ -421,27 +423,28 @@ export default { <div class="btn-group gl-mr-3 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" > - <button + <gl-button :disabled="isSubmitButtonDisabled" - class="btn btn-success js-comment-button js-comment-submit-button qa-comment-button" + class="js-comment-button js-comment-submit-button qa-comment-button" type="submit" + category="primary" + variant="success" :data-track-label="trackingLabel" data-track-event="click_button" @click.prevent="handleSave()" + >{{ commentButtonTitle }}</gl-button > - {{ commentButtonTitle }} - </button> - <button + <gl-button :disabled="isSubmitButtonDisabled" name="button" - type="button" - class="btn btn-success note-type-toggle js-note-new-discussion dropdown-toggle qa-note-dropdown" + category="primary" + variant="success" + class="note-type-toggle js-note-new-discussion dropdown-toggle qa-note-dropdown" data-display="static" data-toggle="dropdown" + icon="chevron-down" :aria-label="__('Open comment type dropdown')" - > - <i aria-hidden="true" class="fa fa-caret-down toggle-icon"></i> - </button> + /> <ul class="note-type-dropdown dropdown-open-top dropdown-menu"> <li :class="{ 'droplab-item-selected': noteType === 'comment' }"> @@ -465,11 +468,7 @@ export default { </li> <li class="divider droplab-item-ignore"></li> <li :class="{ 'droplab-item-selected': noteType === 'discussion' }"> - <button - type="button" - class="btn btn-transparent qa-discussion-option" - @click.prevent="setNoteType('discussion')" - > + <button class="qa-discussion-option" @click.prevent="setNoteType('discussion')"> <i aria-hidden="true" class="fa fa-check icon"></i> <div class="description"> <strong>{{ __('Start thread') }}</strong> @@ -480,17 +479,19 @@ export default { </ul> </div> - <loading-button + <gl-button v-if="canToggleIssueState && !isToggleBlockedIssueWarning" :loading="isToggleStateButtonLoading" - :container-class="[ + category="secondary" + :variant="buttonVariant" + :class="[ actionButtonClassNames, - 'btn btn-comment btn-comment-and-close js-action-button', + 'btn-comment btn-comment-and-close js-action-button', ]" :disabled="isToggleStateButtonLoading || isSubmitting" - :label="issueActionButtonTitle" @click="handleSave(true)" - /> + >{{ issueActionButtonTitle }}</gl-button + > </div> </form> </div> diff --git a/app/assets/javascripts/notes/components/diff_discussion_header.vue b/app/assets/javascripts/notes/components/diff_discussion_header.vue index 50d224a2f08..8e6c01ba63f 100644 --- a/app/assets/javascripts/notes/components/diff_discussion_header.vue +++ b/app/assets/javascripts/notes/components/diff_discussion_header.vue @@ -1,4 +1,5 @@ <script> +/* eslint-disable vue/no-v-html */ import { mapActions } from 'vuex'; import { escape } from 'lodash'; diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue index 8897b54fac7..c01cd8f8037 100644 --- a/app/assets/javascripts/notes/components/diff_with_note.vue +++ b/app/assets/javascripts/notes/components/diff_with_note.vue @@ -1,6 +1,7 @@ <script> +/* eslint-disable vue/no-v-html */ import { mapState, mapActions } from 'vuex'; -import { GlSkeletonLoading } from '@gitlab/ui'; +import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue'; diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue index 4a1a1086329..c6fab271376 100644 --- a/app/assets/javascripts/notes/components/discussion_counter.vue +++ b/app/assets/javascripts/notes/components/discussion_counter.vue @@ -1,7 +1,6 @@ <script> import { mapGetters, mapActions } from 'vuex'; -import { GlTooltipDirective } from '@gitlab/ui'; -import Icon from '~/vue_shared/components/icon.vue'; +import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; import discussionNavigation from '../mixins/discussion_navigation'; export default { @@ -9,7 +8,7 @@ export default { GlTooltip: GlTooltipDirective, }, components: { - Icon, + GlIcon, }, mixins: [discussionNavigation], computed: { @@ -60,7 +59,7 @@ export default { :class="{ 'line-resolve-btn is-active': allResolved, 'line-resolve-text': !allResolved }" > <template v-if="allResolved"> - <icon name="check-circle-filled" /> + <gl-icon name="check-circle-filled" /> {{ __('All threads resolved') }} </template> <template v-else> @@ -79,7 +78,7 @@ export default { :title="s__('Resolve all threads in new issue')" class="new-issue-for-discussion btn btn-default discussion-create-issue-btn" > - <icon name="issue-new" /> + <gl-icon name="issue-new" /> </a> </div> <div v-if="isLoggedIn && !allResolved" class="btn-group btn-group-sm" role="group"> @@ -92,7 +91,7 @@ export default { data-track-property="click_next_unresolved_thread_top" @click="jumpToNextDiscussion" > - <icon name="comment-next" /> + <gl-icon name="comment-next" /> </button> </div> <div class="btn-group btn-group-sm" role="group"> @@ -102,7 +101,7 @@ export default { class="btn btn-default toggle-all-discussions-btn" @click="handleExpandDiscussions" > - <icon :name="allExpanded ? 'angle-up' : 'angle-down'" /> + <gl-icon :name="allExpanded ? 'angle-up' : 'angle-down'" /> </button> </div> </div> diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue index 6b1e3298f9a..989ce9ff144 100644 --- a/app/assets/javascripts/notes/components/discussion_filter.vue +++ b/app/assets/javascripts/notes/components/discussion_filter.vue @@ -1,8 +1,8 @@ <script> import $ from 'jquery'; import { mapGetters, mapActions } from 'vuex'; +import { GlIcon } from '@gitlab/ui'; import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility'; -import Icon from '~/vue_shared/components/icon.vue'; import { DISCUSSION_FILTERS_DEFAULT_VALUE, HISTORY_ONLY_FILTER_VALUE, @@ -14,7 +14,7 @@ import notesEventHub from '../event_hub'; export default { components: { - Icon, + GlIcon, }, props: { filters: { @@ -120,7 +120,7 @@ export default { data-toggle="dropdown" aria-expanded="false" > - {{ currentFilter.title }} <icon name="chevron-down" /> + {{ currentFilter.title }} <gl-icon name="chevron-down" /> </button> <div ref="dropdownMenu" diff --git a/app/assets/javascripts/notes/components/discussion_filter_note.vue b/app/assets/javascripts/notes/components/discussion_filter_note.vue index 8dc4b43d69a..ae6646cf96c 100644 --- a/app/assets/javascripts/notes/components/discussion_filter_note.vue +++ b/app/assets/javascripts/notes/components/discussion_filter_note.vue @@ -1,6 +1,6 @@ <script> -import { GlButton } from '@gitlab/ui'; -import Icon from '~/vue_shared/components/icon.vue'; +/* eslint-disable vue/no-v-html */ +import { GlButton, GlIcon } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import notesEventHub from '../event_hub'; @@ -8,7 +8,7 @@ import notesEventHub from '../event_hub'; export default { components: { GlButton, - Icon, + GlIcon, }, computed: { timelineContent() { @@ -35,7 +35,7 @@ export default { <template> <li class="timeline-entry note note-wrapper discussion-filter-note js-discussion-filter-note"> <div class="timeline-icon d-none d-lg-flex"> - <icon name="comment" /> + <gl-icon name="comment" /> </div> <div class="timeline-content"> <div ref="timelineContent" v-html="timelineContent"></div> diff --git a/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue b/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue index b71ce1b6a0a..f94d0060b41 100644 --- a/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue +++ b/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue @@ -1,12 +1,11 @@ <script> -import { GlTooltipDirective } from '@gitlab/ui'; -import icon from '~/vue_shared/components/icon.vue'; +import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; import discussionNavigation from '../mixins/discussion_navigation'; export default { name: 'JumpToNextDiscussionButton', components: { - icon, + GlIcon, }, directives: { GlTooltip: GlTooltipDirective, @@ -33,7 +32,7 @@ export default { data-track-property="click_next_unresolved_thread" @click="jumpToNextRelativeDiscussion(fromDiscussionId)" > - <icon name="comment-next" /> + <gl-icon name="comment-next" /> </button> </div> </template> diff --git a/app/assets/javascripts/notes/components/discussion_locked_widget.vue b/app/assets/javascripts/notes/components/discussion_locked_widget.vue index 8636984c6af..2f215e36d5b 100644 --- a/app/assets/javascripts/notes/components/discussion_locked_widget.vue +++ b/app/assets/javascripts/notes/components/discussion_locked_widget.vue @@ -1,13 +1,12 @@ <script> -import { GlLink } from '@gitlab/ui'; -import Icon from '~/vue_shared/components/icon.vue'; +import { GlLink, GlIcon } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import Issuable from '~/vue_shared/mixins/issuable'; import issuableStateMixin from '../mixins/issuable_state'; export default { components: { - Icon, + GlIcon, GlLink, }, mixins: [Issuable, issuableStateMixin], @@ -28,7 +27,7 @@ export default { <template> <div class="disabled-comment text-center"> <span class="issuable-note-warning inline"> - <icon :size="16" name="lock" class="icon" /> + <gl-icon :size="16" name="lock" class="icon" /> <span v-if="isProjectArchived"> {{ projectArchivedWarning }} <gl-link :href="archivedProjectDocsPath" target="_blank" class="learn-more"> diff --git a/app/assets/javascripts/notes/components/discussion_resolve_button.vue b/app/assets/javascripts/notes/components/discussion_resolve_button.vue index 77f6f1e51c5..e060a6affd4 100644 --- a/app/assets/javascripts/notes/components/discussion_resolve_button.vue +++ b/app/assets/javascripts/notes/components/discussion_resolve_button.vue @@ -1,10 +1,10 @@ <script> -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlButton } from '@gitlab/ui'; export default { name: 'ResolveDiscussionButton', components: { - GlLoadingIcon, + GlButton, }, props: { isResolving: { @@ -21,8 +21,7 @@ export default { </script> <template> - <button ref="button" type="button" class="btn btn-default ml-sm-2" @click="$emit('onClick')"> - <gl-loading-icon v-if="isResolving" ref="isResolvingIcon" inline /> + <gl-button :loading="isResolving" class="ml-sm-2" @click="$emit('onClick')"> {{ buttonTitle }} - </button> + </gl-button> </template> diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue index a8ae7fb48f0..a8057276f1a 100644 --- a/app/assets/javascripts/notes/components/note_actions.vue +++ b/app/assets/javascripts/notes/components/note_actions.vue @@ -1,18 +1,18 @@ <script> import { mapGetters } from 'vuex'; -import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui'; -import { __ } from '~/locale'; +import { GlLoadingIcon, GlTooltipDirective, GlIcon } from '@gitlab/ui'; +import { __, sprintf } from '~/locale'; import resolvedStatusMixin from '~/batch_comments/mixins/resolved_status'; -import Icon from '~/vue_shared/components/icon.vue'; import ReplyButton from './note_actions/reply_button.vue'; import eventHub from '~/sidebar/event_hub'; import Api from '~/api'; import { deprecatedCreateFlash as flash } from '~/flash'; +import { splitCamelCase } from '../../lib/utils/text_utility'; export default { name: 'NoteActions', components: { - Icon, + GlIcon, ReplyButton, GlLoadingIcon, }, @@ -48,6 +48,26 @@ export default { required: false, default: null, }, + isAuthor: { + type: Boolean, + required: false, + default: false, + }, + isContributor: { + type: Boolean, + required: false, + default: false, + }, + noteableType: { + type: String, + required: false, + default: '', + }, + projectName: { + type: String, + required: false, + default: '', + }, showReply: { type: Boolean, required: true, @@ -122,6 +142,9 @@ export default { targetType() { return this.getNoteableData.targetType; }, + noteableDisplayName() { + return splitCamelCase(this.noteableType).toLowerCase(); + }, assignees() { return this.getNoteableData.assignees || []; }, @@ -131,6 +154,22 @@ export default { canAssign() { return this.getNoteableData.current_user?.can_update && this.isIssue; }, + displayAuthorBadgeText() { + return sprintf(__('This user is the author of this %{noteable}.'), { + noteable: this.noteableDisplayName, + }); + }, + displayMemberBadgeText() { + return sprintf(__('This user is a %{access} of the %{name} project.'), { + access: this.accessLevel.toLowerCase(), + name: this.projectName, + }); + }, + displayContributorBadgeText() { + return sprintf(__('This user has previously committed to the %{name} project.'), { + name: this.projectName, + }); + }, }, methods: { onEdit() { @@ -176,7 +215,24 @@ export default { <template> <div class="note-actions"> - <span v-if="accessLevel" class="note-role user-access-role">{{ accessLevel }}</span> + <span + v-if="isAuthor" + class="note-role user-access-role has-tooltip d-none d-md-inline-block" + :title="displayAuthorBadgeText" + >{{ __('Author') }}</span + > + <span + v-if="accessLevel" + class="note-role user-access-role has-tooltip" + :title="displayMemberBadgeText" + >{{ accessLevel }}</span + > + <span + v-else-if="isContributor" + class="note-role user-access-role has-tooltip" + :title="displayContributorBadgeText" + >{{ __('Contributor') }}</span + > <div v-if="canResolve" class="note-actions-item"> <button ref="resolveButton" @@ -189,7 +245,7 @@ export default { @click="onResolve" > <template v-if="!isResolving"> - <icon :name="isResolved ? 'check-circle-filled' : 'check-circle'" /> + <gl-icon :name="isResolved ? 'check-circle-filled' : 'check-circle'" /> </template> <gl-loading-icon v-else inline /> </button> @@ -203,9 +259,9 @@ export default { title="Add reaction" data-position="right" > - <icon class="link-highlight award-control-icon-neutral" name="slight-smile" /> - <icon class="link-highlight award-control-icon-positive" name="smiley" /> - <icon class="link-highlight award-control-icon-super-positive" name="smiley" /> + <gl-icon class="link-highlight award-control-icon-neutral" name="slight-smile" /> + <gl-icon class="link-highlight award-control-icon-positive" name="smiley" /> + <gl-icon class="link-highlight award-control-icon-super-positive" name="smiley" /> </a> </div> <reply-button @@ -222,7 +278,7 @@ export default { class="note-action-button js-note-edit btn btn-transparent qa-note-edit-button" @click="onEdit" > - <icon name="pencil" class="link-highlight" /> + <gl-icon name="pencil" class="link-highlight" /> </button> </div> <div v-if="showDeleteAction" class="note-actions-item"> @@ -233,7 +289,7 @@ export default { class="note-action-button js-note-delete btn btn-transparent" @click="onDelete" > - <icon name="remove" class="link-highlight" /> + <gl-icon name="remove" class="link-highlight" /> </button> </div> <div v-else-if="shouldShowActionsDropdown" class="dropdown more-actions note-actions-item"> @@ -245,7 +301,7 @@ export default { data-toggle="dropdown" @click="closeTooltip" > - <icon class="icon" name="ellipsis_v" /> + <gl-icon class="icon" name="ellipsis_v" /> </button> <ul class="dropdown-menu more-actions-dropdown dropdown-open-left"> <li v-if="canReportAsAbuse"> diff --git a/app/assets/javascripts/notes/components/note_actions/reply_button.vue b/app/assets/javascripts/notes/components/note_actions/reply_button.vue index 30cb7967c34..f19b7667fb2 100644 --- a/app/assets/javascripts/notes/components/note_actions/reply_button.vue +++ b/app/assets/javascripts/notes/components/note_actions/reply_button.vue @@ -1,12 +1,10 @@ <script> -import { GlTooltipDirective, GlDeprecatedButton } from '@gitlab/ui'; -import Icon from '~/vue_shared/components/icon.vue'; +import { GlTooltipDirective, GlButton } from '@gitlab/ui'; export default { name: 'ReplyButton', components: { - Icon, - GlDeprecatedButton, + GlButton, }, directives: { GlTooltip: GlTooltipDirective, @@ -16,17 +14,17 @@ export default { <template> <div class="note-actions-item"> - <gl-deprecated-button + <gl-button ref="button" v-gl-tooltip - class="note-action-button" data-track-event="click_button" data-track-label="reply_comment_button" - variant="transparent" + category="tertiary" + size="small" + icon="comment" :title="__('Reply to comment')" + :aria-label="__('Reply to comment')" @click="$emit('startReplying')" - > - <icon name="comment" class="link-highlight" /> - </gl-deprecated-button> + /> </div> </template> diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue index 42b78929f8a..314fa762768 100644 --- a/app/assets/javascripts/notes/components/note_body.vue +++ b/app/assets/javascripts/notes/components/note_body.vue @@ -1,4 +1,5 @@ <script> +/* eslint-disable vue/no-v-html */ import { mapActions, mapGetters, mapState } from 'vuex'; import $ from 'jquery'; import '~/behaviors/markdown/render_gfm'; diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index 24227d55ebf..88b4461cf38 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -1,4 +1,5 @@ <script> +/* eslint-disable vue/no-v-html */ import { mapGetters, mapActions, mapState } from 'vuex'; import { mergeUrlParams } from '~/lib/utils/url_utility'; import eventHub from '../event_hub'; @@ -336,7 +337,7 @@ export default { v-model="updatedNoteBody" :data-supports-quick-actions="!isEditing" name="note[note]" - class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input" + class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form qa-reply-input" dir="auto" :aria-label="__('Description')" :placeholder="__('Write a comment or drag your files here…')" diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue index 9ded5ab648e..a13a0dbbf30 100644 --- a/app/assets/javascripts/notes/components/note_header.vue +++ b/app/assets/javascripts/notes/components/note_header.vue @@ -1,6 +1,7 @@ <script> +/* eslint-disable vue/no-v-html */ import { mapActions } from 'vuex'; -import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; +import { GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui'; import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; export default { @@ -9,6 +10,7 @@ export default { GitlabTeamMemberBadge: () => import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'), GlIcon, + GlLoadingIcon, }, directives: { GlTooltip: GlTooltipDirective, @@ -194,13 +196,12 @@ export default { class="gl-ml-1 gl-text-gray-700 align-middle" /> <slot name="extra-controls"></slot> - <i + <gl-loading-icon v-if="showSpinner" ref="spinner" - class="fa fa-spinner fa-spin editing-spinner" - :aria-label="__('Comment is being updated')" - aria-hidden="true" - ></i> + class="editing-spinner" + :label="__('Comment is being updated')" + /> </span> </div> </template> diff --git a/app/assets/javascripts/notes/components/note_signed_out_widget.vue b/app/assets/javascripts/notes/components/note_signed_out_widget.vue index ccfe84ab098..593933016e1 100644 --- a/app/assets/javascripts/notes/components/note_signed_out_widget.vue +++ b/app/assets/javascripts/notes/components/note_signed_out_widget.vue @@ -1,8 +1,12 @@ <script> +import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import { mapGetters } from 'vuex'; import { __, sprintf } from '~/locale'; export default { + directives: { + SafeHtml, + }, computed: { ...mapGetters(['getNotesDataByProp']), registerLink() { @@ -30,5 +34,5 @@ export default { </script> <template> - <div class="disabled-comment text-center" v-html="signedOutText"></div> + <div v-safe-html="signedOutText" class="disabled-comment text-center"></div> </template> diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index b4176c6063b..62ee7f30c57 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -1,10 +1,9 @@ <script> import { mapActions, mapGetters } from 'vuex'; -import { GlTooltipDirective } from '@gitlab/ui'; +import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form'; import { s__, __ } from '~/locale'; import { clearDraft, getDiscussionReplyKey } from '~/lib/utils/autosave'; -import icon from '~/vue_shared/components/icon.vue'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; import DraftNote from '~/batch_comments/components/draft_note.vue'; import { deprecatedCreateFlash as Flash } from '../../flash'; @@ -22,7 +21,7 @@ import DiscussionActions from './discussion_actions.vue'; export default { name: 'NoteableDiscussion', components: { - icon, + GlIcon, userAvatarLink, diffDiscussionHeader, noteSignedOutWidget, diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index ce771e67cbb..4f45fcb0062 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -2,7 +2,7 @@ import $ from 'jquery'; import { mapGetters, mapActions } from 'vuex'; import { escape } from 'lodash'; -import { GlSprintf } from '@gitlab/ui'; +import { GlSprintf, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { truncateSha } from '~/lib/utils/text_utility'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; @@ -34,6 +34,9 @@ export default { NoteBody, TimelineEntryItem, }, + directives: { + SafeHtml, + }, mixins: [noteable, resolvable, glFeatureFlagsMixin()], props: { note: { @@ -358,7 +361,7 @@ export default { </template> </gl-sprintf> </div> - <div v-once class="timeline-icon"> + <div class="timeline-icon"> <user-avatar-link :link-href="author.path" :img-src="author.avatar_url" @@ -371,14 +374,13 @@ export default { <div class="timeline-content"> <div class="note-header"> <note-header - v-once :author="author" :created-at="note.created_at" :note-id="note.id" :is-confidential="note.confidential" > <slot slot="note-header-info" name="note-header-info"></slot> - <span v-if="commit" v-html="actionText"></span> + <span v-if="commit" v-safe-html="actionText"></span> <span v-else-if="note.created_at" class="d-none d-sm-inline">·</span> </note-header> <note-actions @@ -387,6 +389,10 @@ export default { :note-id="note.id" :note-url="note.noteable_note_url" :access-level="note.human_access" + :is-contributor="note.is_contributor" + :is-author="note.is_noteable_author" + :project-name="note.project_name" + :noteable-type="note.noteable_type" :show-reply="showReplyButton" :can-edit="note.current_user.can_edit" :can-award-emoji="note.current_user.can_award_emoji" diff --git a/app/assets/javascripts/notes/components/toggle_replies_widget.vue b/app/assets/javascripts/notes/components/toggle_replies_widget.vue index dd132d4f608..bddac60647d 100644 --- a/app/assets/javascripts/notes/components/toggle_replies_widget.vue +++ b/app/assets/javascripts/notes/components/toggle_replies_widget.vue @@ -1,12 +1,12 @@ <script> import { uniqBy } from 'lodash'; -import Icon from '~/vue_shared/components/icon.vue'; +import { GlIcon } from '@gitlab/ui'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; export default { components: { - Icon, + GlIcon, UserAvatarLink, TimeAgoTooltip, }, @@ -44,7 +44,7 @@ export default { <template> <li :class="className" class="replies-toggle js-toggle-replies"> <template v-if="collapsed"> - <icon name="chevron-right" @click.native="toggle" /> + <gl-icon name="chevron-right" @click.native="toggle" /> <div> <user-avatar-link v-for="author in uniqueAuthors" @@ -71,7 +71,7 @@ export default { class="collapse-replies-btn js-collapse-replies qa-collapse-replies" @click="toggle" > - <icon name="chevron-down" /> {{ s__('Notes|Collapse replies') }} + <gl-icon name="chevron-down" /> {{ s__('Notes|Collapse replies') }} </span> </li> </template> diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js index c1449237f8a..b81aae7c257 100644 --- a/app/assets/javascripts/notes/constants.js +++ b/app/assets/javascripts/notes/constants.js @@ -22,6 +22,8 @@ export const TIME_DIFFERENCE_VALUE = 10; export const ASC = 'asc'; export const DESC = 'desc'; +export const DISCUSSION_FETCH_TIMEOUT = 750; + export const NOTEABLE_TYPE_MAPPING = { Issue: ISSUE_NOTEABLE_TYPE, MergeRequest: MERGE_REQUEST_NOTEABLE_TYPE, diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js index f6069b509e8..9c63a7e3cd4 100644 --- a/app/assets/javascripts/notes/stores/actions.js +++ b/app/assets/javascripts/notes/stores/actions.js @@ -87,6 +87,7 @@ export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFi return axios.get(path, config).then(({ data }) => { commit(types.SET_INITIAL_DISCUSSIONS, data); + commit(types.SET_FETCHING_DISCUSSIONS, false); dispatch('updateResolvableDiscussionsCounts'); }); @@ -136,6 +137,23 @@ export const updateNote = ({ commit, dispatch }, { endpoint, note }) => export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes) => { const { notesById } = getters; + const debouncedFetchDiscussions = isFetching => { + if (!isFetching) { + commit(types.SET_FETCHING_DISCUSSIONS, true); + dispatch('fetchDiscussions', { path: state.notesData.discussionsPath }); + } else { + if (isFetching !== true) { + clearTimeout(state.currentlyFetchingDiscussions); + } + + commit( + types.SET_FETCHING_DISCUSSIONS, + setTimeout(() => { + dispatch('fetchDiscussions', { path: state.notesData.discussionsPath }); + }, constants.DISCUSSION_FETCH_TIMEOUT), + ); + } + }; notes.forEach(note => { if (notesById[note.id]) { @@ -146,7 +164,7 @@ export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes) if (discussion) { commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note); } else if (note.type === constants.DIFF_NOTE) { - dispatch('fetchDiscussions', { path: state.notesData.discussionsPath }); + debouncedFetchDiscussions(state.currentlyFetchingDiscussions); } else { commit(types.ADD_NEW_NOTE, note); } @@ -457,7 +475,7 @@ export const poll = ({ commit, state, getters, dispatch }) => { }); if (!Visibility.hidden()) { - eTagPoll.makeRequest(); + eTagPoll.makeDelayedRequest(2500); } else { dispatch('fetchData'); } diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js index 1649e63c61f..161c9b8b1b5 100644 --- a/app/assets/javascripts/notes/stores/modules/index.js +++ b/app/assets/javascripts/notes/stores/modules/index.js @@ -12,6 +12,7 @@ export default () => ({ lastFetchedAt: null, currentDiscussionId: null, batchSuggestionsInfo: [], + currentlyFetchingDiscussions: false, /** * selectedCommentPosition & selectedCommentPosition structures are the same as `position.line_range`: * { diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js index eb3447291bc..23515cdd9e3 100644 --- a/app/assets/javascripts/notes/stores/mutation_types.js +++ b/app/assets/javascripts/notes/stores/mutation_types.js @@ -36,6 +36,7 @@ export const SET_CURRENT_DISCUSSION_ID = 'SET_CURRENT_DISCUSSION_ID'; export const SET_DISCUSSIONS_SORT = 'SET_DISCUSSIONS_SORT'; export const SET_SELECTED_COMMENT_POSITION = 'SET_SELECTED_COMMENT_POSITION'; export const SET_SELECTED_COMMENT_POSITION_HOVER = 'SET_SELECTED_COMMENT_POSITION_HOVER'; +export const SET_FETCHING_DISCUSSIONS = 'SET_FETCHING_DISCUSSIONS'; // Issue export const CLOSE_ISSUE = 'CLOSE_ISSUE'; diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js index aa078f00569..a8bd94cc763 100644 --- a/app/assets/javascripts/notes/stores/mutations.js +++ b/app/assets/javascripts/notes/stores/mutations.js @@ -379,4 +379,7 @@ export default { [types.UPDATE_ASSIGNEES](state, assignees) { state.noteableData.assignees = assignees; }, + [types.SET_FETCHING_DISCUSSIONS](state, value) { + state.currentlyFetchingDiscussions = value; + }, }; |