diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-11 15:09:05 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-11 15:09:05 +0300 |
commit | 28e90894e1e6f17320f5b1d2fff6fe736bf65dff (patch) | |
tree | 21d63bf124b6064eb1650acc3e2aabe6dbc99f58 /app/assets/javascripts | |
parent | a48957b317edf23b1bcfc6df0c098a824eae86f4 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
13 files changed, 212 insertions, 57 deletions
diff --git a/app/assets/javascripts/ci/runner/components/registration/registration_instructions.vue b/app/assets/javascripts/ci/runner/components/registration/registration_instructions.vue index 6e7c87b8515..69021dde0e9 100644 --- a/app/assets/javascripts/ci/runner/components/registration/registration_instructions.vue +++ b/app/assets/javascripts/ci/runner/components/registration/registration_instructions.vue @@ -70,7 +70,7 @@ export default { captureException({ error, component: this.$options.name }); }, pollInterval() { - if (this.runner?.status === STATUS_ONLINE) { + if (this.isRunnerOnline) { // stop polling return 0; } @@ -97,9 +97,6 @@ export default { } return s__('Runners|Register runner'); }, - status() { - return this.runner?.status; - }, tokenMessage() { if (this.token) { return s__( @@ -122,15 +119,34 @@ export default { runCommand() { return runCommand({ platform: this.platform }); }, + isRunnerOnline() { + return this.runner?.status === STATUS_ONLINE; + }, + }, + created() { + window.addEventListener('beforeunload', this.onBeforeunload); + }, + destroyed() { + window.removeEventListener('beforeunload', this.onBeforeunload); }, methods: { toggleDrawer() { this.$emit('toggleDrawer'); }, + onBeforeunload(event) { + if (this.isRunnerOnline) { + return undefined; + } + + const str = s__('Runners|You may lose access to the runner token if you leave this page.'); + event.preventDefault(); + // eslint-disable-next-line no-param-reassign + event.returnValue = str; // Chrome requires returnValue to be set + return str; + }, }, EXECUTORS_HELP_URL, SERVICE_COMMANDS_HELP_URL, - STATUS_ONLINE, I18N_REGISTRATION_SUCCESS, }; </script> @@ -225,7 +241,7 @@ export default { </gl-sprintf> </p> </section> - <section v-if="status == $options.STATUS_ONLINE"> + <section v-if="isRunnerOnline"> <h2 class="gl-font-size-h2">🎉 {{ $options.I18N_REGISTRATION_SUCCESS }}</h2> <p class="gl-pl-6"> diff --git a/app/assets/javascripts/graphql_shared/issuable_client.js b/app/assets/javascripts/graphql_shared/issuable_client.js index f482fabc5f6..3e310f941ec 100644 --- a/app/assets/javascripts/graphql_shared/issuable_client.js +++ b/app/assets/javascripts/graphql_shared/issuable_client.js @@ -81,6 +81,14 @@ export const config = { }); }, }, + userPermissions: { + read(permission = {}) { + return { + ...permission, + setWorkItemMetadata: false, + }; + }, + }, }, }, MemberInterfaceConnection: { diff --git a/app/assets/javascripts/super_sidebar/components/pinned_section.vue b/app/assets/javascripts/super_sidebar/components/pinned_section.vue index 34b7d95322e..d1c0e757a91 100644 --- a/app/assets/javascripts/super_sidebar/components/pinned_section.vue +++ b/app/assets/javascripts/super_sidebar/components/pinned_section.vue @@ -92,6 +92,6 @@ export default { {{ $options.i18n.emptyHint }} </div> </gl-collapse> - <hr class="gl-my-2" /> + <hr class="gl-my-2 gl-mx-4" /> </section> </template> diff --git a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue index 2bb355736fd..3fdc5124111 100644 --- a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue @@ -134,7 +134,7 @@ export default { <ul class="gl-p-0 gl-m-0"> <nav-item v-for="item in staticItems" :key="item.id" :item="item" is-static /> </ul> - <hr class="gl-my-2" /> + <hr class="gl-my-2 gl-mx-4" /> </section> <pinned-section diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue index 082c261977b..650fa798db6 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue @@ -130,7 +130,7 @@ export default { <span v-if="approvalLeftMessage">{{ message }}</span> <span v-else class="gl-font-weight-bold">{{ message }}</span> <user-avatar-list - class="gl-display-inline-block gl-vertical-align-middle gl-pt-1" + class="gl-display-inline-flex gl-vertical-align-middle" :img-size="24" :items="approvers" /> diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue index 71cf85c75a7..6552a874c3a 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue @@ -90,7 +90,7 @@ export default { </script> <template> - <span ref="userAvatar" class="gl-display-inline-flex"> + <span ref="userAvatar"> <gl-avatar :class="{ lazy: lazy, diff --git a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue index 8a05960869c..713c08e20e9 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue @@ -1,8 +1,8 @@ <script> -import { GlAvatar, GlButton } from '@gitlab/ui'; import * as Sentry from '@sentry/browser'; import Tracking from '~/tracking'; import { ASC } from '~/notes/constants'; +import { __ } from '~/locale'; import { clearDraft } from '~/lib/utils/autosave'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { getWorkItemQuery } from '../../utils'; @@ -17,8 +17,6 @@ export default { avatarUrl: window.gon.current_user_avatar_url, }, components: { - GlAvatar, - GlButton, WorkItemNoteSignedOut, WorkItemCommentLocked, WorkItemCommentForm, @@ -75,11 +73,16 @@ export default { required: false, default: () => ({}), }, + isNewDiscussion: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { workItem: {}, - isEditing: false, + isEditing: this.isNewDiscussion, isSubmitting: false, isSubmittingWithKeydown: false, }; @@ -118,23 +121,9 @@ export default { property: `type_${this.workItemType}`, }; }, - isLockedOutOrSignedOut() { - return !this.signedIn || !this.canUpdate; - }, - lockedOutUserWarningInReplies() { - return this.addPadding && this.isLockedOutOrSignedOut; - }, - timelineEntryClass() { - return { - 'timeline-entry gl-mb-3 note note-wrapper note-comment': true, - 'gl-bg-gray-10 gl-rounded-bottom-left-base gl-rounded-bottom-right-base gl-p-5! gl-mx-n3 gl-mb-n2!': this - .lockedOutUserWarningInReplies, - }; - }, timelineEntryInnerClass() { return { - 'timeline-entry-inner': true, - 'gl-pb-3': this.addPadding, + 'timeline-entry-inner': this.isNewDiscussion, }; }, timelineContentClass() { @@ -155,6 +144,18 @@ export default { canUpdate() { return this.workItem?.userPermissions?.updateWorkItem; }, + workItemState() { + return this.workItem?.state; + }, + commentButtonText() { + return this.isNewDiscussion ? __('Comment') : __('Reply'); + }, + timelineEntryClass() { + return this.isNewDiscussion + ? 'timeline-entry note-form' + : // eslint-disable-next-line @gitlab/require-i18n-strings + 'note note-wrapper note-comment discussion-reply-holder gl-border-t-0! clearfix'; + }, }, watch: { autofocus: { @@ -226,9 +227,13 @@ export default { } }, cancelEditing() { - this.isEditing = false; + this.isEditing = this.isNewDiscussion; this.$emit('cancelEditing'); }, + showReplyForm() { + this.isEditing = true; + this.$emit('startReplying'); + }, }, }; </script> @@ -242,9 +247,6 @@ export default { :is-project-archived="isProjectArchived" /> <div v-else :class="timelineEntryInnerClass"> - <div class="timeline-avatar gl-float-left"> - <gl-avatar :src="$options.constantOptions.avatarUrl" :size="32" class="gl-mr-3" /> - </div> <div :class="timelineContentClass"> <div :class="parentClass"> <work-item-comment-form @@ -253,17 +255,27 @@ export default { :aria-label="__('Add a reply')" :is-submitting="isSubmitting" :autosave-key="autosaveKey" + :is-new-discussion="isNewDiscussion" :autocomplete-data-sources="autocompleteDataSources" :markdown-preview-path="markdownPreviewPath" + :work-item-state="workItemState" + :work-item-id="workItemId" + :autofocus="autofocus" + :comment-button-text="commentButtonText" @submitForm="updateWorkItem" @cancelEditing="cancelEditing" /> - <gl-button + <textarea v-else - class="gl-flex-grow-1 gl-justify-content-start! gl-text-secondary!" - @click="isEditing = true" - >{{ __('Add a reply') }}</gl-button - > + ref="textarea" + rows="1" + class="reply-placeholder-text-field gl-font-regular!" + data-testid="note-reply-textarea" + :placeholder="__('Reply')" + :aria-label="__('Reply to comment')" + @focus="showReplyForm" + @click="showReplyForm" + ></textarea> </div> </div> </div> diff --git a/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue b/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue index 8390ae5b2e1..f9f24366725 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue @@ -1,10 +1,22 @@ <script> import { GlButton } from '@gitlab/ui'; +import * as Sentry from '@sentry/browser'; import { helpPagePath } from '~/helpers/help_page_helper'; -import { s__, __ } from '~/locale'; +import { s__, __, sprintf } from '~/locale'; +import Tracking from '~/tracking'; +import { + I18N_WORK_ITEM_ERROR_UPDATING, + sprintfWorkItem, + STATE_OPEN, + STATE_EVENT_REOPEN, + STATE_EVENT_CLOSE, + TRACKING_CATEGORY_SHOW, + i18n, +} from '~/work_items/constants'; import { getDraft, clearDraft, updateDraft } from '~/lib/utils/autosave'; import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue'; +import { getUpdateWorkItemMutation } from '~/work_items/components/update_work_item'; export default { constantOptions: { @@ -14,8 +26,13 @@ export default { GlButton, MarkdownEditor, }, + mixins: [Tracking.mixin()], inject: ['fullPath'], props: { + workItemId: { + type: String, + required: true, + }, workItemType: { type: String, required: true, @@ -52,13 +69,36 @@ export default { required: false, default: () => ({}), }, + isNewDiscussion: { + type: Boolean, + required: false, + default: false, + }, + workItemState: { + type: String, + required: false, + default: STATE_OPEN, + }, + autofocus: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { commentText: getDraft(this.autosaveKey) || this.initialValue || '', + updateInProgress: false, }; }, computed: { + tracking() { + return { + category: TRACKING_CATEGORY_SHOW, + label: 'work_item_task_status', + property: `type_${this.workItemType}`, + }; + }, formFieldProps() { return { 'aria-label': this.ariaLabel, @@ -67,6 +107,17 @@ export default { name: 'work-item-add-or-edit-comment', }; }, + isWorkItemOpen() { + return this.workItemState === STATE_OPEN; + }, + toggleWorkItemStateText() { + return this.isWorkItemOpen + ? sprintf(__('Close %{workItemType}'), { workItemType: this.workItemType.toLowerCase() }) + : sprintf(__('Reopen %{workItemType}'), { workItemType: this.workItemType.toLowerCase() }); + }, + cancelButtonText() { + return this.isNewDiscussion ? this.toggleWorkItemStateText : __('Cancel'); + }, }, methods: { setCommentText(newText) { @@ -99,13 +150,55 @@ export default { this.$emit('cancelEditing'); clearDraft(this.autosaveKey); }, + async toggleWorkItemState() { + const input = { + id: this.workItemId, + stateEvent: this.isWorkItemOpen ? STATE_EVENT_CLOSE : STATE_EVENT_REOPEN, + }; + + this.updateInProgress = true; + + try { + this.track('updated_state'); + + const { mutation, variables } = getUpdateWorkItemMutation({ + workItemParentId: this.workItemParentId, + input, + }); + + const { data } = await this.$apollo.mutate({ + mutation, + variables, + }); + + const errors = data.workItemUpdate?.errors; + + if (errors?.length) { + this.$emit('error', i18n.updateError); + } + } catch (error) { + const msg = sprintfWorkItem(I18N_WORK_ITEM_ERROR_UPDATING, this.workItemType); + + this.$emit('error', msg); + Sentry.captureException(error); + } + + this.updateInProgress = false; + }, + cancelButtonAction() { + if (this.isNewDiscussion) { + this.toggleWorkItemState(); + } else { + this.cancelEditing(); + } + }, }, }; </script> <template> - <div class="timeline-discussion-body"> - <div class="note-body"> + <div class="timeline-discussion-body gl-overflow-visible!"> + <div class="note-body gl-p-0! gl-overflow-visible!"> <form class="common-note-form gfm-form js-main-target-form gl-flex-grow-1"> <markdown-editor :value="commentText" @@ -113,11 +206,12 @@ export default { :markdown-docs-path="$options.constantOptions.markdownDocsPath" :autocomplete-data-sources="autocompleteDataSources" :form-field-props="formFieldProps" + :add-spacing-classes="false" data-testid="work-item-add-comment" class="gl-mb-3" - autofocus use-bottom-toolbar supports-quick-actions + :autofocus="autofocus" @input="setCommentText" @keydown.meta.enter="$emit('submitForm', commentText)" @keydown.ctrl.enter="$emit('submitForm', commentText)" @@ -127,6 +221,7 @@ export default { category="primary" variant="confirm" data-testid="confirm-button" + :disabled="!commentText.length" :loading="isSubmitting" @click="$emit('submitForm', commentText)" >{{ commentButtonText }} @@ -135,8 +230,9 @@ export default { data-testid="cancel-button" category="primary" class="gl-ml-3" - @click="cancelEditing" - >{{ __('Cancel') }} + :loading="updateInProgress" + @click="cancelButtonAction" + >{{ cancelButtonText }} </gl-button> </form> </div> diff --git a/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue b/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue index 6cf15ba50ec..21fc8f99366 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue @@ -248,6 +248,7 @@ export default { :add-padding="true" :autocomplete-data-sources="autocompleteDataSources" :markdown-preview-path="markdownPreviewPath" + @startReplying="showReplyForm" @cancelEditing="hideReplyForm" @replied="onReplied" @replying="onReplying" diff --git a/app/assets/javascripts/work_items/components/notes/work_item_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_note.vue index 8b25d305398..5ccc5526ce8 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_note.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_note.vue @@ -310,6 +310,9 @@ export default { :comment-button-text="__('Save comment')" :autocomplete-data-sources="autocompleteDataSources" :markdown-preview-path="markdownPreviewPath" + :work-item-id="workItemId" + :autofocus="isEditing" + class="gl-pl-3 gl-mt-3" @cancelEditing="isEditing = false" @submitForm="updateNote" /> diff --git a/app/assets/javascripts/work_items/components/work_item_notes.vue b/app/assets/javascripts/work_items/components/work_item_notes.vue index a1f1eda8bc5..00cdc224320 100644 --- a/app/assets/javascripts/work_items/components/work_item_notes.vue +++ b/app/assets/javascripts/work_items/components/work_item_notes.vue @@ -1,6 +1,7 @@ <script> import { GlSkeletonLoader, GlModal } from '@gitlab/ui'; import * as Sentry from '@sentry/browser'; +import { uniqueId } from 'lodash'; import { __ } from '~/locale'; import { scrollToTargetOnResize } from '~/lib/utils/resize_observer'; import { TYPENAME_DISCUSSION, TYPENAME_NOTE } from '~/graphql_shared/constants'; @@ -96,6 +97,7 @@ export default { sortOrder: ASC, noteToDelete: null, discussionFilter: WORK_ITEM_NOTES_FILTER_ALL_NOTES, + addNoteKey: uniqueId(`work-item-add-note-${this.workItemId}`), }; }, computed: { @@ -134,6 +136,7 @@ export default { fetchByIid: this.fetchByIid, workItemType: this.workItemType, sortOrder: this.sortOrder, + isNewDiscussion: true, markdownPreviewPath: this.markdownPreviewPath, autocompleteDataSources: this.autocompleteDataSources, }; @@ -278,6 +281,9 @@ export default { filterDiscussions(filterValue) { this.discussionFilter = filterValue; }, + updateKey() { + this.addNoteKey = uniqueId(`work-item-add-note-${this.workItemId}`); + }, async fetchMoreNotes() { this.isLoadingMore = true; // copied from discussions batch logic - every fetchMore call has a higher @@ -361,12 +367,17 @@ export default { </div> <div v-else class="issuable-discussion gl-mb-5 gl-clearfix!"> <template v-if="!initialLoading"> - <ul class="notes main-notes-list timeline gl-clearfix!"> - <work-item-add-note - v-if="formAtTop && !commentsDisabled" - v-bind="workItemCommentFormProps" - @error="$emit('error', $event)" - /> + <div v-if="formAtTop && !commentsDisabled" class="js-comment-form"> + <ul class="notes notes-form timeline"> + <work-item-add-note + v-bind="workItemCommentFormProps" + :key="addNoteKey" + @cancelEditing="updateKey" + @error="$emit('error', $event)" + /> + </ul> + </div> + <ul class="notes main-notes-list timeline"> <template v-for="discussion in notesArray"> <system-note v-if="isSystemNote(discussion)" @@ -393,17 +404,21 @@ export default { </template> </template> - <work-item-add-note - v-if="!formAtTop && !commentsDisabled" - v-bind="workItemCommentFormProps" - @error="$emit('error', $event)" - /> - <work-item-history-only-filter-note v-if="commentsDisabled" @changeFilter="filterDiscussions" /> </ul> + <div v-if="!formAtTop && !commentsDisabled" class="js-comment-form"> + <ul class="notes notes-form timeline"> + <work-item-add-note + v-bind="workItemCommentFormProps" + :key="addNoteKey" + @cancelEditing="updateKey" + @error="$emit('error', $event)" + /> + </ul> + </div> </template> <template v-if="showLoadingMoreSkeleton"> diff --git a/app/assets/javascripts/work_items/graphql/typedefs.graphql b/app/assets/javascripts/work_items/graphql/typedefs.graphql index fda71fabe22..40fb0fbc91d 100644 --- a/app/assets/javascripts/work_items/graphql/typedefs.graphql +++ b/app/assets/javascripts/work_items/graphql/typedefs.graphql @@ -15,6 +15,10 @@ extend type WorkItem { mockWidgets: [LocalWorkItemWidget] } +extend type WorkItemPermissions { + setWorkItemMetadata: Boolean +} + input LocalUserInput { id: ID! name: String diff --git a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql index 3651cad48f6..86640a6d994 100644 --- a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql +++ b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql @@ -27,7 +27,7 @@ fragment WorkItem on WorkItem { userPermissions { deleteWorkItem updateWorkItem - setWorkItemMetadata + setWorkItemMetadata @client } widgets { ...WorkItemWidgets |