diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-18 13:34:06 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-18 13:34:06 +0300 |
commit | 859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch) | |
tree | d7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /app/assets/javascripts/snippets | |
parent | 446d496a6d000c73a304be52587cd9bbc7493136 (diff) |
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'app/assets/javascripts/snippets')
15 files changed, 121 insertions, 34 deletions
diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue index ffb5e242973..9f43ac36df7 100644 --- a/app/assets/javascripts/snippets/components/edit.vue +++ b/app/assets/javascripts/snippets/components/edit.vue @@ -1,27 +1,28 @@ <script> import { GlButton, GlLoadingIcon } from '@gitlab/ui'; +import eventHub from '~/blob/components/eventhub'; import { deprecatedCreateFlash as Flash } from '~/flash'; -import { __, sprintf } from '~/locale'; -import TitleField from '~/vue_shared/components/form/title.vue'; import { redirectTo, joinPaths } from '~/lib/utils/url_utility'; -import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue'; +import { __, sprintf } from '~/locale'; import { SNIPPET_MARK_EDIT_APP_START, SNIPPET_MEASURE_BLOBS_CONTENT, } from '~/performance/constants'; -import eventHub from '~/blob/components/eventhub'; import { performanceMarkAndMeasure } from '~/performance/utils'; +import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue'; +import TitleField from '~/vue_shared/components/form/title.vue'; -import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql'; -import CreateSnippetMutation from '../mutations/createSnippet.mutation.graphql'; -import { getSnippetMixin } from '../mixins/snippets'; import { SNIPPET_CREATE_MUTATION_ERROR, SNIPPET_UPDATE_MUTATION_ERROR } from '../constants'; +import { getSnippetMixin } from '../mixins/snippets'; +import CreateSnippetMutation from '../mutations/createSnippet.mutation.graphql'; +import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql'; import { markBlobPerformance } from '../utils/blob'; +import { getErrorMessage } from '../utils/error'; import SnippetBlobActionsEdit from './snippet_blob_actions_edit.vue'; -import SnippetVisibilityEdit from './snippet_visibility_edit.vue'; import SnippetDescriptionEdit from './snippet_description_edit.vue'; +import SnippetVisibilityEdit from './snippet_visibility_edit.vue'; eventHub.$on(SNIPPET_MEASURE_BLOBS_CONTENT, markBlobPerformance); @@ -32,6 +33,7 @@ export default { SnippetBlobActionsEdit, TitleField, FormFooterActions, + CaptchaModal: () => import('~/captcha/captcha_modal.vue'), GlButton, GlLoadingIcon, }, @@ -66,12 +68,25 @@ export default { description: '', visibilityLevel: this.selectedLevel, }, + captchaResponse: '', + needsCaptchaResponse: false, + captchaSiteKey: '', + spamLogId: '', }; }, computed: { hasBlobChanges() { return this.actions.length > 0; }, + hasNoChanges() { + return ( + this.actions.every( + (action) => !action.content && !action.filePath && !action.previousPath, + ) && + !this.snippet.title && + !this.snippet.description + ); + }, hasValidBlobs() { return this.actions.every((x) => x.content); }, @@ -88,6 +103,8 @@ export default { description: this.snippet.description, visibilityLevel: this.snippet.visibilityLevel, blobActions: this.actions, + ...(this.spamLogId && { spamLogId: this.spamLogId }), + ...(this.captchaResponse && { captchaResponse: this.captchaResponse }), }; }, saveButtonLabel() { @@ -116,7 +133,7 @@ export default { onBeforeUnload(e = {}) { const returnValue = __('Are you sure you want to lose unsaved changes?'); - if (!this.hasBlobChanges || this.isUpdating) return undefined; + if (!this.hasBlobChanges || this.hasNoChanges || this.isUpdating) return undefined; Object.assign(e, { returnValue }); return returnValue; @@ -159,6 +176,13 @@ export default { .then(({ data }) => { const baseObj = this.newSnippet ? data?.createSnippet : data?.updateSnippet; + if (baseObj.needsCaptchaResponse) { + // If we need a captcha response, start process for receiving captcha response. + // We will resubmit after the response is obtained. + this.requestCaptchaResponse(baseObj.captchaSiteKey, baseObj.spamLogId); + return; + } + const errors = baseObj?.errors; if (errors.length) { this.flashAPIFailure(errors[0]); @@ -167,12 +191,44 @@ export default { } }) .catch((e) => { - this.flashAPIFailure(e); + // eslint-disable-next-line no-console + console.error('[gitlab] unexpected error while updating snippet', e); + + this.flashAPIFailure(getErrorMessage(e)); }); }, updateActions(actions) { this.actions = actions; }, + /** + * Start process for getting captcha response from user + * + * @param captchaSiteKey Stored in data and used to display the captcha. + * @param spamLogId Stored in data and included when the form is re-submitted. + */ + requestCaptchaResponse(captchaSiteKey, spamLogId) { + this.captchaSiteKey = captchaSiteKey; + this.spamLogId = spamLogId; + this.needsCaptchaResponse = true; + }, + /** + * Handle the captcha response from the user + * + * @param captchaResponse The captchaResponse value emitted from the modal. + */ + receivedCaptchaResponse(captchaResponse) { + this.needsCaptchaResponse = false; + this.captchaResponse = captchaResponse; + + if (this.captchaResponse) { + // If the user solved the captcha resubmit the form. + this.handleFormSubmit(); + } else { + // If the user didn't solve the captcha (e.g. they just closed the modal), + // finish the update and allow them to continue editing or manually resubmit the form. + this.isUpdating = false; + } + }, }, }; </script> @@ -190,6 +246,11 @@ export default { class="loading-animation prepend-top-20 gl-mb-6" /> <template v-else> + <captcha-modal + :captcha-site-key="captchaSiteKey" + :needs-captcha-response="needsCaptchaResponse" + @receivedCaptchaResponse="receivedCaptchaResponse" + /> <title-field id="snippet-title" v-model="snippet.title" diff --git a/app/assets/javascripts/snippets/components/embed_dropdown.vue b/app/assets/javascripts/snippets/components/embed_dropdown.vue index a5d2c337d67..f6c9c569b5f 100644 --- a/app/assets/javascripts/snippets/components/embed_dropdown.vue +++ b/app/assets/javascripts/snippets/components/embed_dropdown.vue @@ -1,5 +1,4 @@ <script> -import { escape as esc } from 'lodash'; import { GlButton, GlDropdown, @@ -8,6 +7,7 @@ import { GlFormInputGroup, GlTooltipDirective, } from '@gitlab/ui'; +import { escape as esc } from 'lodash'; import { __ } from '~/locale'; const MSG_EMBED = __('Embed'); diff --git a/app/assets/javascripts/snippets/components/show.vue b/app/assets/javascripts/snippets/components/show.vue index a3e5535c5fa..46629a569ec 100644 --- a/app/assets/javascripts/snippets/components/show.vue +++ b/app/assets/javascripts/snippets/components/show.vue @@ -1,20 +1,20 @@ <script> import { GlLoadingIcon } from '@gitlab/ui'; -import EmbedDropdown from './embed_dropdown.vue'; -import SnippetHeader from './snippet_header.vue'; -import SnippetTitle from './snippet_title.vue'; -import SnippetBlob from './snippet_blob_view.vue'; -import CloneDropdownButton from '~/vue_shared/components/clone_dropdown.vue'; -import { SNIPPET_VISIBILITY_PUBLIC } from '~/snippets/constants'; +import eventHub from '~/blob/components/eventhub'; import { SNIPPET_MARK_VIEW_APP_START, SNIPPET_MEASURE_BLOBS_CONTENT, } from '~/performance/constants'; import { performanceMarkAndMeasure } from '~/performance/utils'; -import eventHub from '~/blob/components/eventhub'; +import { SNIPPET_VISIBILITY_PUBLIC } from '~/snippets/constants'; +import CloneDropdownButton from '~/vue_shared/components/clone_dropdown.vue'; import { getSnippetMixin } from '../mixins/snippets'; import { markBlobPerformance } from '../utils/blob'; +import EmbedDropdown from './embed_dropdown.vue'; +import SnippetBlob from './snippet_blob_view.vue'; +import SnippetHeader from './snippet_header.vue'; +import SnippetTitle from './snippet_title.vue'; eventHub.$on(SNIPPET_MEASURE_BLOBS_CONTENT, markBlobPerformance); diff --git a/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue b/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue index ff27c90a84d..d221195ddc7 100644 --- a/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue +++ b/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue @@ -2,9 +2,9 @@ import { GlButton } from '@gitlab/ui'; import { cloneDeep } from 'lodash'; import { s__, sprintf } from '~/locale'; -import SnippetBlobEdit from './snippet_blob_edit.vue'; import { SNIPPET_MAX_BLOBS } from '../constants'; import { createBlob, decorateBlob, diffAll } from '../utils/blob'; +import SnippetBlobEdit from './snippet_blob_edit.vue'; export default { components: { diff --git a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue index c8545e334a6..4fb27397039 100644 --- a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue +++ b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue @@ -1,12 +1,12 @@ <script> import { GlLoadingIcon } from '@gitlab/ui'; import BlobHeaderEdit from '~/blob/components/blob_edit_header.vue'; -import EditorLite from '~/vue_shared/components/editor_lite.vue'; -import { getBaseURL, joinPaths } from '~/lib/utils/url_utility'; -import axios from '~/lib/utils/axios_utils'; -import { SNIPPET_BLOB_CONTENT_FETCH_ERROR } from '~/snippets/constants'; import { deprecatedCreateFlash as Flash } from '~/flash'; +import axios from '~/lib/utils/axios_utils'; +import { getBaseURL, joinPaths } from '~/lib/utils/url_utility'; import { sprintf } from '~/locale'; +import { SNIPPET_BLOB_CONTENT_FETCH_ERROR } from '~/snippets/constants'; +import EditorLite from '~/vue_shared/components/editor_lite.vue'; export default { components: { diff --git a/app/assets/javascripts/snippets/components/snippet_blob_view.vue b/app/assets/javascripts/snippets/components/snippet_blob_view.vue index 4326c3c3159..27b3a30b40a 100644 --- a/app/assets/javascripts/snippets/components/snippet_blob_view.vue +++ b/app/assets/javascripts/snippets/components/snippet_blob_view.vue @@ -1,8 +1,8 @@ <script> import GetBlobContent from 'shared_queries/snippet/snippet_blob_content.query.graphql'; -import BlobHeader from '~/blob/components/blob_header.vue'; import BlobContent from '~/blob/components/blob_content.vue'; +import BlobHeader from '~/blob/components/blob_header.vue'; import { SIMPLE_BLOB_VIEWER, @@ -31,8 +31,10 @@ export default { }, result() { if (this.activeViewerType === RICH_BLOB_VIEWER) { + // eslint-disable-next-line vue/no-mutating-props this.blob.richViewer.renderError = null; } else { + // eslint-disable-next-line vue/no-mutating-props this.blob.simpleViewer.renderError = null; } }, diff --git a/app/assets/javascripts/snippets/components/snippet_description_edit.vue b/app/assets/javascripts/snippets/components/snippet_description_edit.vue index 5e6caf27bdd..bac423f6838 100644 --- a/app/assets/javascripts/snippets/components/snippet_description_edit.vue +++ b/app/assets/javascripts/snippets/components/snippet_description_edit.vue @@ -1,7 +1,7 @@ <script> import { GlFormInput } from '@gitlab/ui'; -import MarkdownField from '~/vue_shared/components/markdown/field.vue'; import setupCollapsibleInputs from '~/snippet/collapsible_input'; +import MarkdownField from '~/vue_shared/components/markdown/field.vue'; export default { components: { diff --git a/app/assets/javascripts/snippets/components/snippet_header.vue b/app/assets/javascripts/snippets/components/snippet_header.vue index 5ba62908b43..bf19b63650e 100644 --- a/app/assets/javascripts/snippets/components/snippet_header.vue +++ b/app/assets/javascripts/snippets/components/snippet_header.vue @@ -11,14 +11,14 @@ import { GlButton, GlTooltipDirective, } from '@gitlab/ui'; -import CanCreatePersonalSnippet from 'shared_queries/snippet/user_permissions.query.graphql'; import CanCreateProjectSnippet from 'shared_queries/snippet/project_permissions.query.graphql'; +import CanCreatePersonalSnippet from 'shared_queries/snippet/user_permissions.query.graphql'; +import { fetchPolicies } from '~/lib/graphql'; +import { joinPaths } from '~/lib/utils/url_utility'; import { __ } from '~/locale'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import DeleteSnippetMutation from '../mutations/deleteSnippet.mutation.graphql'; -import { joinPaths } from '~/lib/utils/url_utility'; -import { fetchPolicies } from '~/lib/graphql'; export default { components: { diff --git a/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue b/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue index ee5076835ab..18a7d4ad218 100644 --- a/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue +++ b/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue @@ -1,7 +1,7 @@ <script> import { GlIcon, GlFormGroup, GlFormRadio, GlFormRadioGroup, GlLink } from '@gitlab/ui'; -import { defaultSnippetVisibilityLevels } from '../utils/blob'; import { SNIPPET_LEVELS_RESTRICTED, SNIPPET_LEVELS_DISABLED } from '~/snippets/constants'; +import { defaultSnippetVisibilityLevels } from '../utils/blob'; export default { components: { diff --git a/app/assets/javascripts/snippets/index.js b/app/assets/javascripts/snippets/index.js index 853ccb0c2fe..789332ce5b7 100644 --- a/app/assets/javascripts/snippets/index.js +++ b/app/assets/javascripts/snippets/index.js @@ -1,9 +1,9 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import Translate from '~/vue_shared/translate'; import createDefaultClient from '~/lib/graphql'; import { SNIPPET_LEVELS_MAP, SNIPPET_VISIBILITY_PRIVATE } from '~/snippets/constants'; +import Translate from '~/vue_shared/translate'; Vue.use(VueApollo); Vue.use(Translate); diff --git a/app/assets/javascripts/snippets/mixins/snippets.js b/app/assets/javascripts/snippets/mixins/snippets.js index 89a88958152..7552eae97fc 100644 --- a/app/assets/javascripts/snippets/mixins/snippets.js +++ b/app/assets/javascripts/snippets/mixins/snippets.js @@ -11,10 +11,14 @@ export const getSnippetMixin = { ids: [this.snippetGid], }; }, - update: (data) => { + update(data) { const res = data.snippets.nodes[0]; + + // Set `snippet.blobs` since some child components are coupled to this. if (res) { - res.blobs = res.blobs.nodes; + // It's possible for us to not get any blobs in a response. + // In this case, we should default to current blobs. + res.blobs = res.blobs ? res.blobs.nodes : this.blobs; } return res; diff --git a/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql b/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql index f688868d1b9..64d5d7c30fa 100644 --- a/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql +++ b/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql @@ -4,5 +4,7 @@ mutation CreateSnippet($input: CreateSnippetInput!) { snippet { webUrl } + needsCaptchaResponse + captchaSiteKey } } diff --git a/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql b/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql index 548725f7357..0a72f71b7c9 100644 --- a/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql +++ b/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql @@ -4,5 +4,8 @@ mutation UpdateSnippet($input: UpdateSnippetInput!) { snippet { webUrl } + needsCaptchaResponse + captchaSiteKey + spamLogId } } diff --git a/app/assets/javascripts/snippets/utils/blob.js b/app/assets/javascripts/snippets/utils/blob.js index a47418323f2..2a3f590a803 100644 --- a/app/assets/javascripts/snippets/utils/blob.js +++ b/app/assets/javascripts/snippets/utils/blob.js @@ -1,4 +1,6 @@ import { uniqueId } from 'lodash'; +import { SNIPPET_MARK_BLOBS_CONTENT, SNIPPET_MEASURE_BLOBS_CONTENT } from '~/performance/constants'; +import { performanceMarkAndMeasure } from '~/performance/utils'; import { SNIPPET_BLOB_ACTION_CREATE, SNIPPET_BLOB_ACTION_UPDATE, @@ -7,8 +9,6 @@ import { SNIPPET_LEVELS_MAP, SNIPPET_VISIBILITY, } from '../constants'; -import { performanceMarkAndMeasure } from '~/performance/utils'; -import { SNIPPET_MARK_BLOBS_CONTENT, SNIPPET_MEASURE_BLOBS_CONTENT } from '~/performance/constants'; const createLocalId = () => uniqueId('blob_local_'); diff --git a/app/assets/javascripts/snippets/utils/error.js b/app/assets/javascripts/snippets/utils/error.js new file mode 100644 index 00000000000..2d5c2a64038 --- /dev/null +++ b/app/assets/javascripts/snippets/utils/error.js @@ -0,0 +1,15 @@ +import { isString } from 'lodash'; +import { __ } from '~/locale'; + +export const UNEXPECTED_ERROR = __('Unexpected error'); + +export const getErrorMessage = (e) => { + if (!e) { + return UNEXPECTED_ERROR; + } + if (isString(e)) { + return e; + } + + return e.message || e.networkError || UNEXPECTED_ERROR; +}; |