diff options
Diffstat (limited to 'app/assets/javascripts/notes')
12 files changed, 133 insertions, 66 deletions
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 7213658bdf2..9504ed78778 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -14,7 +14,7 @@ import $ from 'jquery'; import { mapActions, mapGetters, mapState } from 'vuex'; import Autosave from '~/autosave'; import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; -import { deprecatedCreateFlash as Flash } from '~/flash'; +import createFlash from '~/flash'; import { statusBoxState } from '~/issuable/components/status_box.vue'; import httpStatusCodes from '~/lib/utils/http_status'; import { @@ -293,7 +293,11 @@ export default { toggleState() .then(() => statusBoxState.updateStatus && statusBoxState.updateStatus()) .then(refreshUserMergeRequestCounts) - .catch(() => Flash(constants.toggleStateErrorMessage[this.noteableType][this.openState])); + .catch(() => + createFlash({ + message: constants.toggleStateErrorMessage[this.noteableType][this.openState], + }), + ); }, discard(shouldClear = true) { // `blur` is needed to clear slash commands autocomplete cache if event fired. diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue index dfe2763d8bd..0892276ff3b 100644 --- a/app/assets/javascripts/notes/components/discussion_notes.vue +++ b/app/assets/javascripts/notes/components/discussion_notes.vue @@ -130,15 +130,18 @@ export default { @handleDeleteNote="$emit('deleteNote')" @startReplying="$emit('startReplying')" > - <note-edited-text - v-if="discussion.resolved" - slot="discussion-resolved-text" - :edited-at="discussion.resolved_at" - :edited-by="discussion.resolved_by" - :action-text="resolvedText" - class-name="discussion-headline-light js-discussion-headline discussion-resolved-text" - /> - <slot slot="avatar-badge" name="avatar-badge"></slot> + <template #discussion-resolved-text> + <note-edited-text + v-if="discussion.resolved" + :edited-at="discussion.resolved_at" + :edited-by="discussion.resolved_by" + :action-text="resolvedText" + class-name="discussion-headline-light js-discussion-headline discussion-resolved-text" + /> + </template> + <template #avatar-badge> + <slot name="avatar-badge"></slot> + </template> </component> <discussion-notes-replies-wrapper :is-diff-discussion="discussion.diff_discussion"> <toggle-replies-widget @@ -175,7 +178,9 @@ export default { :discussion-resolve-path="discussion.resolve_path" @handleDeleteNote="$emit('deleteNote')" > - <slot v-if="index === 0" slot="avatar-badge" name="avatar-badge"></slot> + <template #avatar-badge> + <slot v-if="index === 0" name="avatar-badge"></slot> + </template> </component> <slot :show-replies="isExpanded || !hasReplies" name="footer"></slot> </template> diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue index 0f72b4f2dba..44d0c741d5a 100644 --- a/app/assets/javascripts/notes/components/note_actions.vue +++ b/app/assets/javascripts/notes/components/note_actions.vue @@ -3,7 +3,7 @@ import { GlTooltipDirective, GlIcon, GlButton, GlDropdownItem } from '@gitlab/ui import { mapActions, mapGetters } from 'vuex'; import Api from '~/api'; import resolvedStatusMixin from '~/batch_comments/mixins/resolved_status'; -import { deprecatedCreateFlash as flash } from '~/flash'; +import createFlash from '~/flash'; import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; import { __, sprintf } from '~/locale'; import eventHub from '~/sidebar/event_hub'; @@ -234,7 +234,11 @@ export default { assignee_ids: assignees.map((assignee) => assignee.id), }) .then(() => this.handleAssigneeUpdate(assignees)) - .catch(() => flash(__('Something went wrong while updating assignees'))); + .catch(() => + createFlash({ + message: __('Something went wrong while updating assignees'), + }), + ); } }, setAwardEmoji(awardName) { diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue index 9eb7b928ea4..835750cc137 100644 --- a/app/assets/javascripts/notes/components/note_awards_list.vue +++ b/app/assets/javascripts/notes/components/note_awards_list.vue @@ -1,8 +1,8 @@ <script> import { mapActions, mapGetters } from 'vuex'; +import createFlash from '~/flash'; import { __ } from '~/locale'; import AwardsList from '~/vue_shared/components/awards_list.vue'; -import { deprecatedCreateFlash as Flash } from '../../flash'; export default { components: { @@ -48,7 +48,11 @@ export default { awardName, }; - this.toggleAwardRequest(data).catch(() => Flash(__('Something went wrong on our end.'))); + this.toggleAwardRequest(data).catch(() => + createFlash({ + message: __('Something went wrong on our end.'), + }), + ); }, }, }; diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue index 6932af61c69..1a4a6c137a6 100644 --- a/app/assets/javascripts/notes/components/note_header.vue +++ b/app/assets/javascripts/notes/components/note_header.vue @@ -216,6 +216,7 @@ export default { <gl-loading-icon v-if="showSpinner" ref="spinner" + size="sm" class="editing-spinner" :label="__('Comment is being updated')" /> diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 1af9e4be373..b99579fb9a7 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -2,11 +2,12 @@ import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; import { mapActions, mapGetters } from 'vuex'; import DraftNote from '~/batch_comments/components/draft_note.vue'; +import createFlash from '~/flash'; import { clearDraft, getDiscussionReplyKey } from '~/lib/utils/autosave'; +import { isLoggedIn } from '~/lib/utils/common_utils'; import { s__, __ } from '~/locale'; import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; -import { deprecatedCreateFlash as Flash } from '../../flash'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import eventHub from '../event_hub'; import noteable from '../mixins/noteable'; @@ -85,7 +86,7 @@ export default { return this.getUserData; }, isLoggedIn() { - return Boolean(gon.current_user_id); + return isLoggedIn(); }, autosaveKey() { return getDiscussionReplyKey(this.firstNote.noteable_type, this.discussion.id); @@ -220,7 +221,10 @@ export default { const msg = __( 'Your comment could not be submitted! Please check your network connection and try again.', ); - Flash(msg, 'alert', this.$el); + createFlash({ + message: msg, + parent: this.$el, + }); this.$refs.noteForm.note = noteText; callback(err); }); @@ -262,7 +266,9 @@ export default { @startReplying="showReplyForm" @deleteNote="deleteNoteHandler" > - <slot slot="avatar-badge" name="avatar-badge"></slot> + <template #avatar-badge> + <slot name="avatar-badge"></slot> + </template> <template #footer="{ showReplies }"> <draft-note v-if="showDraft(discussion.reply_id)" diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index 0feb77be653..5ea431224ce 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -4,15 +4,16 @@ import $ from 'jquery'; import { escape, isEmpty } from 'lodash'; import { mapGetters, mapActions } from 'vuex'; import { INLINE_DIFF_LINES_KEY } from '~/diffs/constants'; +import createFlash from '~/flash'; import httpStatusCodes from '~/lib/utils/http_status'; import { truncateSha } from '~/lib/utils/text_utility'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; -import { deprecatedCreateFlash as Flash } from '../../flash'; import { __, s__, sprintf } from '../../locale'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import eventHub from '../event_hub'; import noteable from '../mixins/noteable'; import resolvable from '../mixins/resolvable'; +import { renderMarkdown } from '../utils'; import { getStartLineNumber, getEndLineNumber, @@ -247,7 +248,9 @@ export default { this.isDeleting = false; }) .catch(() => { - Flash(__('Something went wrong while deleting your note. Please try again.')); + createFlash({ + message: __('Something went wrong while deleting your note. Please try again.'), + }); this.isDeleting = false; }); } @@ -298,7 +301,7 @@ export default { this.isRequesting = true; this.oldContent = this.note.note_html; // eslint-disable-next-line vue/no-mutating-props - this.note.note_html = escape(noteText); + this.note.note_html = renderMarkdown(noteText); this.updateNote(data) .then(() => { @@ -316,7 +319,10 @@ export default { this.setSelectedCommentPositionHover(); this.$nextTick(() => { const msg = __('Something went wrong while editing your comment. Please try again.'); - Flash(msg, 'alert', this.$el); + createFlash({ + message: msg, + parent: this.$el, + }); this.recoverNoteContent(noteText); callback(); }); @@ -387,7 +393,9 @@ export default { :img-alt="author.name" :img-size="40" > - <slot slot="avatar-badge" name="avatar-badge"></slot> + <template #avatar-badge> + <slot name="avatar-badge"></slot> + </template> </user-avatar-link> </div> <div class="timeline-content"> @@ -398,7 +406,9 @@ export default { :note-id="note.id" :is-confidential="note.confidential" > - <slot slot="note-header-info" name="note-header-info"></slot> + <template #note-header-info> + <slot name="note-header-info"></slot> + </template> <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> diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index 433f75a752d..29c60b96d8a 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -1,13 +1,13 @@ <script> import { mapGetters, mapActions } from 'vuex'; import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user'; +import createFlash from '~/flash'; import { __ } from '~/locale'; import initUserPopovers from '~/user_popovers'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; import OrderedLayout from '~/vue_shared/components/ordered_layout.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import draftNote from '../../batch_comments/components/draft_note.vue'; -import { deprecatedCreateFlash as Flash } from '../../flash'; import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility'; import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue'; import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue'; @@ -66,6 +66,7 @@ export default { data() { return { currentFilter: null, + renderSkeleton: !this.shouldShow, }; }, computed: { @@ -93,7 +94,7 @@ export default { return this.noteableData.noteableType; }, allDiscussions() { - if (this.isLoading) { + if (this.renderSkeleton || this.isLoading) { const prerenderedNotesCount = parseInt(this.notesData.prerenderedNotesCount, 10) || 0; return new Array(prerenderedNotesCount).fill({ @@ -122,6 +123,10 @@ export default { if (!this.isNotesFetched) { this.fetchNotes(); } + + setTimeout(() => { + this.renderSkeleton = !this.shouldShow; + }); }, discussionTabCounterText(val) { if (this.discussionsCount) { @@ -216,7 +221,9 @@ export default { .catch(() => { this.setLoadingState(false); this.setNotesFetchedState(true); - Flash(__('Something went wrong while fetching comments. Please try again.')); + createFlash({ + message: __('Something went wrong while fetching comments. Please try again.'), + }); }); }, initPolling() { diff --git a/app/assets/javascripts/notes/mixins/resolvable.js b/app/assets/javascripts/notes/mixins/resolvable.js index 27ed8e203b0..9783def1b46 100644 --- a/app/assets/javascripts/notes/mixins/resolvable.js +++ b/app/assets/javascripts/notes/mixins/resolvable.js @@ -1,4 +1,4 @@ -import { deprecatedCreateFlash as Flash } from '~/flash'; +import createFlash from '~/flash'; import { __ } from '~/locale'; export default { @@ -46,7 +46,10 @@ export default { this.isResolving = false; const msg = __('Something went wrong while resolving this discussion. Please try again.'); - Flash(msg, 'alert', this.$el); + createFlash({ + message: msg, + parent: this.$el, + }); }); }, }, diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js index 086e9122c60..6a4a3263e4a 100644 --- a/app/assets/javascripts/notes/stores/actions.js +++ b/app/assets/javascripts/notes/stores/actions.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import Visibility from 'visibilityjs'; import Vue from 'vue'; import Api from '~/api'; +import createFlash from '~/flash'; import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants'; import axios from '~/lib/utils/axios_utils'; import { __, sprintf } from '~/locale'; @@ -9,7 +10,6 @@ import { confidentialWidget } from '~/sidebar/components/confidential/sidebar_co import updateIssueLockMutation from '~/sidebar/components/lock/mutations/update_issue_lock.mutation.graphql'; import updateMergeRequestLockMutation from '~/sidebar/components/lock/mutations/update_merge_request_lock.mutation.graphql'; import loadAwardsHandler from '../../awards_handler'; -import { deprecatedCreateFlash as Flash } from '../../flash'; import { isInViewport, scrollToElement, isInMRPage } from '../../lib/utils/common_utils'; import Poll from '../../lib/utils/poll'; import { create } from '../../lib/utils/recurrence'; @@ -312,25 +312,23 @@ export const saveNote = ({ commit, dispatch }, noteData) => { $('.notes-form .flash-container').hide(); // hide previous flash notification commit(types.REMOVE_PLACEHOLDER_NOTES); // remove previous placeholders - if (replyId) { - if (hasQuickActions) { - placeholderText = utils.stripQuickActions(placeholderText); - } + if (hasQuickActions) { + placeholderText = utils.stripQuickActions(placeholderText); + } - if (placeholderText.length) { - commit(types.SHOW_PLACEHOLDER_NOTE, { - noteBody: placeholderText, - replyId, - }); - } + if (placeholderText.length) { + commit(types.SHOW_PLACEHOLDER_NOTE, { + noteBody: placeholderText, + replyId, + }); + } - if (hasQuickActions) { - commit(types.SHOW_PLACEHOLDER_NOTE, { - isSystemNote: true, - noteBody: utils.getQuickActionText(note), - replyId, - }); - } + if (hasQuickActions) { + commit(types.SHOW_PLACEHOLDER_NOTE, { + isSystemNote: true, + noteBody: utils.getQuickActionText(note), + replyId, + }); } const processQuickActions = (res) => { @@ -354,7 +352,11 @@ export const saveNote = ({ commit, dispatch }, noteData) => { $('.js-gfm-input').trigger('clear-commands-cache.atwho'); - Flash(message || __('Commands applied'), 'notice', noteData.flashContainer); + createFlash({ + message: message || __('Commands applied'), + type: 'notice', + parent: noteData.flashContainer, + }); } return res; @@ -375,11 +377,10 @@ export const saveNote = ({ commit, dispatch }, noteData) => { awardsHandler.scrollToAwards(); }) .catch(() => { - Flash( - __('Something went wrong while adding your award. Please try again.'), - 'alert', - noteData.flashContainer, - ); + createFlash({ + message: __('Something went wrong while adding your award. Please try again.'), + parent: noteData.flashContainer, + }); }) .then(() => res); }; @@ -397,9 +398,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => { }; const removePlaceholder = (res) => { - if (replyId) { - commit(types.REMOVE_PLACEHOLDER_NOTES); - } + commit(types.REMOVE_PLACEHOLDER_NOTES); return res; }; @@ -417,7 +416,10 @@ export const saveNote = ({ commit, dispatch }, noteData) => { const errorMsg = sprintf(__('Your comment could not be submitted because %{error}'), { error: base[0].toLowerCase(), }); - Flash(errorMsg, 'alert', noteData.flashContainer); + createFlash({ + message: errorMsg, + parent: noteData.flashContainer, + }); return { ...data, hasFlash: true }; } } @@ -480,7 +482,9 @@ export const poll = ({ commit, state, getters, dispatch }) => { }); notePollOccurrenceTracking.handle(2, () => { // On the second failure in a row, show the alert and try one more time (hoping to succeed and clear the error) - flashContainer = Flash(__('Something went wrong while fetching latest comments.')); + flashContainer = createFlash({ + message: __('Something went wrong while fetching latest comments.'), + }); setTimeout(() => eTagPoll.restart(), NOTES_POLLING_INTERVAL); }); @@ -570,7 +574,9 @@ export const filterDiscussion = ({ dispatch }, { path, filter, persistFilter }) .catch(() => { dispatch('setLoadingState', false); dispatch('setNotesFetchedState', true); - Flash(__('Something went wrong while fetching comments. Please try again.')); + createFlash({ + message: __('Something went wrong while fetching comments. Please try again.'), + }); }); }; @@ -613,7 +619,10 @@ export const submitSuggestion = ( const flashMessage = errorMessage || defaultMessage; - Flash(__(flashMessage), 'alert', flashContainer); + createFlash({ + message: __(flashMessage), + parent: flashContainer, + }); }) .finally(() => { commit(types.SET_RESOLVING_DISCUSSION, false); @@ -646,7 +655,10 @@ export const submitSuggestionBatch = ({ commit, dispatch, state }, { flashContai const flashMessage = errorMessage || defaultMessage; - Flash(__(flashMessage), 'alert', flashContainer); + createFlash({ + message: __(flashMessage), + parent: flashContainer, + }); }) .finally(() => { commit(types.SET_APPLYING_BATCH_STATE, false); @@ -685,7 +697,9 @@ export const fetchDescriptionVersion = ({ dispatch }, { endpoint, startingVersio }) .catch((error) => { dispatch('receiveDescriptionVersionError', error); - Flash(__('Something went wrong while fetching description changes. Please try again.')); + createFlash({ + message: __('Something went wrong while fetching description changes. Please try again.'), + }); }); }; @@ -717,7 +731,9 @@ export const softDeleteDescriptionVersion = ( }) .catch((error) => { dispatch('receiveDeleteDescriptionVersionError', error); - Flash(__('Something went wrong while deleting description changes. Please try again.')); + createFlash({ + message: __('Something went wrong while deleting description changes. Please try again.'), + }); // Throw an error here because a component like SystemNote - // needs to know if the request failed to reset its internal state. diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js index b04b1d28ffa..956221d69ae 100644 --- a/app/assets/javascripts/notes/stores/getters.js +++ b/app/assets/javascripts/notes/stores/getters.js @@ -279,7 +279,7 @@ export const getDiscussion = (state) => (discussionId) => export const commentsDisabled = (state) => state.commentsDisabled; export const suggestionsCount = (state, getters) => - Object.values(getters.notesById).filter((n) => n.suggestions.length).length; + Object.values(getters.notesById).filter((n) => n.suggestions?.length).length; export const hasDrafts = (state, getters, rootState, rootGetters) => Boolean(rootGetters['batchComments/hasDrafts']); diff --git a/app/assets/javascripts/notes/utils.js b/app/assets/javascripts/notes/utils.js index 7966a884eab..ec18a570960 100644 --- a/app/assets/javascripts/notes/utils.js +++ b/app/assets/javascripts/notes/utils.js @@ -1,4 +1,7 @@ /* eslint-disable @gitlab/require-i18n-strings */ +import marked from 'marked'; +import { sanitize } from '~/lib/dompurify'; +import { markdownConfig } from '~/lib/utils/text_utility'; /** * Tracks snowplow event when User toggles timeline view @@ -10,3 +13,7 @@ export const trackToggleTimelineView = (enabled) => ({ label: 'Status', property: enabled, }); + +export const renderMarkdown = (rawMarkdown) => { + return sanitize(marked(rawMarkdown), markdownConfig); +}; |