diff options
author | Phil Hughes <me@iamphill.com> | 2018-04-18 12:59:37 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-04-18 12:59:37 +0100 |
commit | fb3e23b06da10a90149aba0e2dbc3bca04286b0e (patch) | |
tree | 48b56085d0792225c41c6076556879bb863bc478 /app/assets/javascripts | |
parent | 35d280450260bad53022b6022ece7d31b5f4bd1f (diff) | |
parent | 9c2f6e04cf5d2eb89d0ec68e8ace87b3804e9ae5 (diff) | |
download | gitlab-ce-fb3e23b06da10a90149aba0e2dbc3bca04286b0e.tar.gz |
Merge branch 'master' into ide-staged-changes
Diffstat (limited to 'app/assets/javascripts')
17 files changed, 441 insertions, 291 deletions
diff --git a/app/assets/javascripts/branches/branches_delete_modal.js b/app/assets/javascripts/branches/branches_delete_modal.js index 839e369eaf6..f34496f84c6 100644 --- a/app/assets/javascripts/branches/branches_delete_modal.js +++ b/app/assets/javascripts/branches/branches_delete_modal.js @@ -16,6 +16,7 @@ class DeleteModal { bindEvents() { this.$toggleBtns.on('click', this.setModalData.bind(this)); this.$confirmInput.on('input', this.setDeleteDisabled.bind(this)); + this.$deleteBtn.on('click', this.setDisableDeleteButton.bind(this)); } setModalData(e) { @@ -30,6 +31,16 @@ class DeleteModal { this.$deleteBtn.attr('disabled', e.currentTarget.value !== this.branchName); } + setDisableDeleteButton(e) { + if (this.$deleteBtn.is('[disabled]')) { + e.preventDefault(); + e.stopPropagation(); + return false; + } + + return true; + } + updateModal() { this.$branchName.text(this.branchName); this.$confirmInput.val(''); diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index f8dcdf3f60a..9c12b89240c 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -1,96 +1,102 @@ <script> - import _ from 'underscore'; - import { s__, sprintf } from '../../locale'; - import applicationRow from './application_row.vue'; - import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; - import { - APPLICATION_INSTALLED, - INGRESS, - } from '../constants'; +import _ from 'underscore'; +import { s__, sprintf } from '../../locale'; +import applicationRow from './application_row.vue'; +import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; +import { APPLICATION_INSTALLED, INGRESS } from '../constants'; - export default { - components: { - applicationRow, - clipboardButton, +export default { + components: { + applicationRow, + clipboardButton, + }, + props: { + applications: { + type: Object, + required: false, + default: () => ({}), }, - props: { - applications: { - type: Object, - required: false, - default: () => ({}), - }, - helpPath: { - type: String, - required: false, - default: '', - }, - ingressHelpPath: { - type: String, - required: false, - default: '', - }, - ingressDnsHelpPath: { - type: String, - required: false, - default: '', - }, - managePrometheusPath: { - type: String, - required: false, - default: '', - }, + helpPath: { + type: String, + required: false, + default: '', }, - computed: { - generalApplicationDescription() { - return sprintf( - _.escape(s__( + ingressHelpPath: { + type: String, + required: false, + default: '', + }, + ingressDnsHelpPath: { + type: String, + required: false, + default: '', + }, + managePrometheusPath: { + type: String, + required: false, + default: '', + }, + }, + computed: { + generalApplicationDescription() { + return sprintf( + _.escape( + s__( `ClusterIntegration|Install applications on your Kubernetes cluster. Read more about %{helpLink}`, - )), { - helpLink: `<a href="${this.helpPath}"> + ), + ), + { + helpLink: `<a href="${this.helpPath}"> ${_.escape(s__('ClusterIntegration|installing applications'))} </a>`, - }, - false, - ); - }, - ingressId() { - return INGRESS; - }, - ingressInstalled() { - return this.applications.ingress.status === APPLICATION_INSTALLED; - }, - ingressExternalIp() { - return this.applications.ingress.externalIp; - }, - ingressDescription() { - const extraCostParagraph = sprintf( - _.escape(s__( + }, + false, + ); + }, + ingressId() { + return INGRESS; + }, + ingressInstalled() { + return this.applications.ingress.status === APPLICATION_INSTALLED; + }, + ingressExternalIp() { + return this.applications.ingress.externalIp; + }, + ingressDescription() { + const extraCostParagraph = sprintf( + _.escape( + s__( `ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on - the hosting provider your Kubernetes cluster is installed on. If you are using GKE, - you can %{pricingLink}.`, - )), { - boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`, - pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb" target="_blank" rel="noopener noreferrer"> + the hosting provider your Kubernetes cluster is installed on. If you are using + Google Kubernetes Engine, you can %{pricingLink}.`, + ), + ), + { + boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`, + pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb" target="_blank" rel="noopener noreferrer"> ${_.escape(s__('ClusterIntegration|check the pricing here'))}</a>`, - }, - false, - ); + }, + false, + ); - const externalIpParagraph = sprintf( - _.escape(s__( + const externalIpParagraph = sprintf( + _.escape( + s__( `ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}`, - )), { - ingressHelpLink: `<a href="${this.ingressHelpPath}"> + ), + ), + { + ingressHelpLink: `<a href="${this.ingressHelpPath}"> ${_.escape(s__('ClusterIntegration|More information'))} </a>`, - }, - false, - ); + }, + false, + ); - return ` + return ` <p> ${extraCostParagraph} </p> @@ -98,22 +104,25 @@ ${externalIpParagraph} </p> `; - }, - prometheusDescription() { - return sprintf( - _.escape(s__( + }, + prometheusDescription() { + return sprintf( + _.escape( + s__( `ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications.`, - )), { - gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html" + ), + ), + { + gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html" target="_blank" rel="noopener noreferrer"> ${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`, - }, - false, - ); - }, + }, + false, + ); }, - }; + }, +}; </script> <template> @@ -205,7 +214,7 @@ > {{ s__(`ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes - cluster or Quotas on GKE if it takes a long time.`) }} + cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }} <a :href="ingressHelpPath" diff --git a/app/assets/javascripts/ide/components/changed_file_icon.vue b/app/assets/javascripts/ide/components/changed_file_icon.vue index fbcd0834809..9ce653f6e81 100644 --- a/app/assets/javascripts/ide/components/changed_file_icon.vue +++ b/app/assets/javascripts/ide/components/changed_file_icon.vue @@ -1,12 +1,12 @@ <script> import tooltip from '~/vue_shared/directives/tooltip'; -import icon from '~/vue_shared/components/icon.vue'; +import Icon from '~/vue_shared/components/icon.vue'; import { pluralize } from '~/lib/utils/text_utility'; import { __, sprintf } from '~/locale'; export default { components: { - icon, + Icon, }, directives: { tooltip, diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue index 2cbd982af19..45321df191c 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue @@ -1,41 +1,27 @@ <script> - import { mapState } from 'vuex'; - import { sprintf, __ } from '~/locale'; - import * as consts from '../../stores/modules/commit/constants'; - import RadioGroup from './radio_group.vue'; +import { mapState } from 'vuex'; +import { sprintf, __ } from '~/locale'; +import * as consts from '../../stores/modules/commit/constants'; +import RadioGroup from './radio_group.vue'; - export default { - components: { - RadioGroup, +export default { + components: { + RadioGroup, + }, + computed: { + ...mapState(['currentBranchId']), + commitToCurrentBranchText() { + return sprintf( + __('Commit to %{branchName} branch'), + { branchName: `<strong class="monospace">${this.currentBranchId}</strong>` }, + false, + ); }, - computed: { - ...mapState([ - 'currentBranchId', - ]), - newMergeRequestHelpText() { - return sprintf( - __('Creates a new branch from %{branchName} and re-directs to create a new merge request'), - { branchName: this.currentBranchId }, - ); - }, - commitToCurrentBranchText() { - return sprintf( - __('Commit to %{branchName} branch'), - { branchName: `<strong>${this.currentBranchId}</strong>` }, - false, - ); - }, - commitToNewBranchText() { - return sprintf( - __('Creates a new branch from %{branchName}'), - { branchName: this.currentBranchId }, - ); - }, - }, - commitToCurrentBranch: consts.COMMIT_TO_CURRENT_BRANCH, - commitToNewBranch: consts.COMMIT_TO_NEW_BRANCH, - commitToNewBranchMR: consts.COMMIT_TO_NEW_BRANCH_MR, - }; + }, + commitToCurrentBranch: consts.COMMIT_TO_CURRENT_BRANCH, + commitToNewBranch: consts.COMMIT_TO_NEW_BRANCH, + commitToNewBranchMR: consts.COMMIT_TO_NEW_BRANCH_MR, +}; </script> <template> @@ -53,13 +39,11 @@ :value="$options.commitToNewBranch" :label="__('Create a new branch')" :show-input="true" - :help-text="commitToNewBranchText" /> <radio-group :value="$options.commitToNewBranchMR" :label="__('Create a new branch and merge request')" :show-input="true" - :help-text="newMergeRequestHelpText" /> </div> </template> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue new file mode 100644 index 00000000000..dcd934f76b7 --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue @@ -0,0 +1,130 @@ +<script> +import { __, sprintf } from '../../../locale'; +import Icon from '../../../vue_shared/components/icon.vue'; +import popover from '../../../vue_shared/directives/popover'; +import { MAX_TITLE_LENGTH, MAX_BODY_LENGTH } from '../../constants'; + +export default { + directives: { + popover, + }, + components: { + Icon, + }, + props: { + text: { + type: String, + required: true, + }, + }, + data() { + return { + scrollTop: 0, + isFocused: false, + }; + }, + computed: { + allLines() { + return this.text.split('\n').map((line, i) => ({ + text: line.substr(0, this.getLineLength(i)) || ' ', + highlightedText: line.substr(this.getLineLength(i)), + })); + }, + }, + methods: { + handleScroll() { + if (this.$refs.textarea) { + this.$nextTick(() => { + this.scrollTop = this.$refs.textarea.scrollTop; + }); + } + }, + getLineLength(i) { + return i === 0 ? MAX_TITLE_LENGTH : MAX_BODY_LENGTH; + }, + onInput(e) { + this.$emit('input', e.target.value); + }, + updateIsFocused(isFocused) { + this.isFocused = isFocused; + }, + }, + popoverOptions: { + trigger: 'hover', + placement: 'top', + content: sprintf( + __(` + The character highligher helps you keep the subject line to %{titleLength} characters + and wrap the body at %{bodyLength} so they are readable in git. + `), + { titleLength: MAX_TITLE_LENGTH, bodyLength: MAX_BODY_LENGTH }, + ), + }, +}; +</script> + +<template> + <fieldset class="common-note-form ide-commit-message-field"> + <div + class="md-area" + :class="{ + 'is-focused': isFocused + }" + > + <div + v-once + class="md-header" + > + <ul class="nav-links"> + <li> + {{ __('Commit Message') }} + <span + v-popover="$options.popoverOptions" + class="help-block prepend-left-10" + > + <icon + name="question" + /> + </span> + </li> + </ul> + </div> + <div class="ide-commit-message-textarea-container"> + <div class="ide-commit-message-highlights-container"> + <div + class="note-textarea highlights monospace" + :style="{ + transform: `translate3d(0, ${-scrollTop}px, 0)` + }" + > + <div + v-for="(line, index) in allLines" + :key="index" + > + <span + v-text="line.text" + > + </span><mark + v-show="line.highlightedText" + v-text="line.highlightedText" + > + </mark> + </div> + </div> + </div> + <textarea + class="note-textarea ide-commit-message-textarea" + name="commit-message" + :placeholder="__('Write a commit message...')" + :value="text" + @scroll="handleScroll" + @input="onInput" + @focus="updateIsFocused(true)" + @blur="updateIsFocused(false)" + ref="textarea" + > + </textarea> + </div> + </div> + </fieldset> +</template> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue index 4310d762c78..b660a2961cb 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue @@ -1,52 +1,40 @@ <script> - import { mapActions, mapState, mapGetters } from 'vuex'; - import tooltip from '~/vue_shared/directives/tooltip'; +import { mapActions, mapState, mapGetters } from 'vuex'; +import tooltip from '~/vue_shared/directives/tooltip'; - export default { - directives: { - tooltip, +export default { + directives: { + tooltip, + }, + props: { + value: { + type: String, + required: true, }, - props: { - value: { - type: String, - required: true, - }, - label: { - type: String, - required: false, - default: null, - }, - checked: { - type: Boolean, - required: false, - default: false, - }, - showInput: { - type: Boolean, - required: false, - default: false, - }, - helpText: { - type: String, - required: false, - default: null, - }, + label: { + type: String, + required: false, + default: null, }, - computed: { - ...mapState('commit', [ - 'commitAction', - ]), - ...mapGetters('commit', [ - 'newBranchName', - ]), + checked: { + type: Boolean, + required: false, + default: false, }, - methods: { - ...mapActions('commit', [ - 'updateCommitAction', - 'updateBranchName', - ]), + showInput: { + type: Boolean, + required: false, + default: false, }, - }; + }, + computed: { + ...mapState('commit', ['commitAction']), + ...mapGetters('commit', ['newBranchName']), + }, + methods: { + ...mapActions('commit', ['updateCommitAction', 'updateBranchName']), + }, +}; </script> <template> @@ -65,18 +53,6 @@ {{ label }} </template> <slot v-else></slot> - <span - v-if="helpText" - v-tooltip - class="help-block inline" - :title="helpText" - > - <i - class="fa fa-question-circle" - aria-hidden="true" - > - </i> - </span> </span> </label> <div @@ -85,7 +61,7 @@ > <input type="text" - class="form-control" + class="form-control monospace" :placeholder="newBranchName" @input="updateBranchName($event.target.value)" /> diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 5b82ce605db..32b6af892d8 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -6,8 +6,9 @@ import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue'; import commitFilesList from './commit_sidebar/list.vue'; import EmptyState from './commit_sidebar/empty_state.vue'; -import Actions from './commit_sidebar/actions.vue'; +import CommitMessageField from './commit_sidebar/message_field.vue'; import * as consts from '../stores/modules/commit/constants'; +import Actions from './commit_sidebar/actions.vue'; export default { components: { @@ -17,6 +18,7 @@ export default { EmptyState, Actions, LoadingButton, + CommitMessageField, }, directives: { tooltip, @@ -92,16 +94,10 @@ export default { @submit.prevent.stop="commitChanges" v-if="!rightPanelCollapsed" > - <div class="multi-file-commit-fieldset"> - <textarea - class="form-control multi-file-commit-message" - name="commit-message" - :value="commitMessage" - :placeholder="__('Write a commit message...')" - @input="updateCommitMessage($event.target.value)" - > - </textarea> - </div> + <commit-message-field + :text="commitMessage" + @input="updateCommitMessage" + /> <div class="clearfix prepend-top-15"> <actions /> <loading-button diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js new file mode 100644 index 00000000000..b60d042e0be --- /dev/null +++ b/app/assets/javascripts/ide/constants.js @@ -0,0 +1,3 @@ +// Fuzzy file finder +export const MAX_TITLE_LENGTH = 50; +export const MAX_BODY_LENGTH = 72; diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js index 20983666b4a..4a0a303d5a6 100644 --- a/app/assets/javascripts/ide/ide_router.js +++ b/app/assets/javascripts/ide/ide_router.js @@ -36,11 +36,11 @@ const router = new VueRouter({ base: `${gon.relative_url_root}/-/ide/`, routes: [ { - path: '/project/:namespace/:project', + path: '/project/:namespace/:project+', component: EmptyRouterComponent, children: [ { - path: ':targetmode/:branch/*', + path: ':targetmode(edit|tree|blob)/:branch/*', component: EmptyRouterComponent, }, { diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js index b3882cb8d21..4eb23b2ee0f 100644 --- a/app/assets/javascripts/ide/stores/actions/project.js +++ b/app/assets/javascripts/ide/stores/actions/project.js @@ -5,45 +5,71 @@ import * as types from '../mutation_types'; export const getProjectData = ( { commit, state, dispatch }, { namespace, projectId, force = false } = {}, -) => new Promise((resolve, reject) => { - if (!state.projects[`${namespace}/${projectId}`] || force) { - commit(types.TOGGLE_LOADING, { entry: state }); - service.getProjectData(namespace, projectId) - .then(res => res.data) - .then((data) => { +) => + new Promise((resolve, reject) => { + if (!state.projects[`${namespace}/${projectId}`] || force) { commit(types.TOGGLE_LOADING, { entry: state }); - commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data }); - if (!state.currentProjectId) commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`); - resolve(data); - }) - .catch(() => { - flash('Error loading project data. Please try again.', 'alert', document, null, false, true); - reject(new Error(`Project not loaded ${namespace}/${projectId}`)); - }); - } else { - resolve(state.projects[`${namespace}/${projectId}`]); - } -}); + service + .getProjectData(namespace, projectId) + .then(res => res.data) + .then(data => { + commit(types.TOGGLE_LOADING, { entry: state }); + commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data }); + if (!state.currentProjectId) + commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`); + resolve(data); + }) + .catch(() => { + flash( + 'Error loading project data. Please try again.', + 'alert', + document, + null, + false, + true, + ); + reject(new Error(`Project not loaded ${namespace}/${projectId}`)); + }); + } else { + resolve(state.projects[`${namespace}/${projectId}`]); + } + }); export const getBranchData = ( { commit, state, dispatch }, { projectId, branchId, force = false } = {}, -) => new Promise((resolve, reject) => { - if ((typeof state.projects[`${projectId}`] === 'undefined' || - !state.projects[`${projectId}`].branches[branchId]) - || force) { - service.getBranchData(`${projectId}`, branchId) - .then(({ data }) => { - const { id } = data.commit; - commit(types.SET_BRANCH, { projectPath: `${projectId}`, branchName: branchId, branch: data }); - commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id }); - resolve(data); - }) - .catch(() => { - flash('Error loading branch data. Please try again.', 'alert', document, null, false, true); - reject(new Error(`Branch not loaded - ${projectId}/${branchId}`)); - }); - } else { - resolve(state.projects[`${projectId}`].branches[branchId]); - } -}); +) => + new Promise((resolve, reject) => { + if ( + typeof state.projects[`${projectId}`] === 'undefined' || + !state.projects[`${projectId}`].branches[branchId] || + force + ) { + service + .getBranchData(`${projectId}`, branchId) + .then(({ data }) => { + const { id } = data.commit; + commit(types.SET_BRANCH, { + projectPath: `${projectId}`, + branchName: branchId, + branch: data, + }); + commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id }); + commit(types.SET_CURRENT_BRANCH, branchId); + resolve(data); + }) + .catch(() => { + flash( + 'Error loading branch data. Please try again.', + 'alert', + document, + null, + false, + true, + ); + reject(new Error(`Branch not loaded - ${projectId}/${branchId}`)); + }); + } else { + resolve(state.projects[`${projectId}`].branches[branchId]); + } + }); diff --git a/app/assets/javascripts/ide/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js index 7f7e470c9bb..1176c040fb9 100644 --- a/app/assets/javascripts/ide/stores/mutations/tree.js +++ b/app/assets/javascripts/ide/stores/mutations/tree.js @@ -17,12 +17,8 @@ export default { }); }, [types.SET_DIRECTORY_DATA](state, { data, treePath }) { - Object.assign(state, { - trees: Object.assign(state.trees, { - [treePath]: { - tree: data, - }, - }), + Object.assign(state.trees[treePath], { + tree: data, }); }, [types.SET_LAST_COMMIT_URL](state, { tree = state, url }) { diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index b0573510ff9..2121907dff0 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -19,7 +19,6 @@ import AjaxCache from '~/lib/utils/ajax_cache'; import Vue from 'vue'; import syntaxHighlight from '~/syntax_highlight'; import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; -import { __ } from '~/locale'; import axios from './lib/utils/axios_utils'; import { getLocationHash } from './lib/utils/url_utility'; import Flash from './flash'; @@ -198,6 +197,8 @@ export default class Notes { ); this.$wrapperEl.on('click', '.js-toggle-lazy-diff', this.loadLazyDiff); + this.$wrapperEl.on('click', '.js-toggle-lazy-diff-retry-button', this.onClickRetryLazyLoad.bind(this)); + // fetch notes when tab becomes visible this.$wrapperEl.on('visibilitychange', this.visibilityChange); // when issue status changes, we need to refresh data @@ -244,6 +245,7 @@ export default class Notes { this.$wrapperEl.off('click', '.js-comment-resolve-button'); this.$wrapperEl.off('click', '.system-note-commit-list-toggler'); this.$wrapperEl.off('click', '.js-toggle-lazy-diff'); + this.$wrapperEl.off('click', '.js-toggle-lazy-diff-retry-button'); this.$wrapperEl.off('ajax:success', '.js-main-target-form'); this.$wrapperEl.off('ajax:success', '.js-discussion-note-form'); this.$wrapperEl.off('ajax:complete', '.js-main-target-form'); @@ -1431,16 +1433,15 @@ export default class Notes { syntaxHighlight(fileHolder); } - static renderDiffError($container) { - $container.find('.line_content').html( - $(` - <div class="nothing-here-block"> - ${__( - 'Unable to load the diff.', - )} <a class="js-toggle-lazy-diff" href="javascript:void(0)">Try again</a>? - </div> - `), - ); + onClickRetryLazyLoad(e) { + const $retryButton = $(e.currentTarget); + + $retryButton.prop('disabled', true); + + return this.loadLazyDiff(e) + .then(() => { + $retryButton.prop('disabled', false); + }); } loadLazyDiff(e) { @@ -1449,20 +1450,35 @@ export default class Notes { $container.find('.js-toggle-lazy-diff').removeClass('js-toggle-lazy-diff'); - const tableEl = $container.find('tbody'); - if (tableEl.length === 0) return; + const $tableEl = $container.find('tbody'); + if ($tableEl.length === 0) return; const fileHolder = $container.find('.file-holder'); const url = fileHolder.data('linesPath'); - axios + const $errorContainer = $container.find('.js-error-lazy-load-diff'); + const $successContainer = $container.find('.js-success-lazy-load'); + + /** + * We only fetch resolved discussions. + * Unresolved discussions don't have an endpoint being provided. + */ + if (url) { + return axios .get(url) .then(({ data }) => { + // Reset state in case last request returned error + $successContainer.removeClass('hidden'); + $errorContainer.addClass('hidden'); + Notes.renderDiffContent($container, data); }) .catch(() => { - Notes.renderDiffError($container); + $successContainer.addClass('hidden'); + $errorContainer.removeClass('hidden'); }); + } + return Promise.resolve(); } toggleCommitList(e) { diff --git a/app/assets/javascripts/performance_bar/services/performance_bar_service.js b/app/assets/javascripts/performance_bar/services/performance_bar_service.js index 3ebfaa87a4e..bc71911ae35 100644 --- a/app/assets/javascripts/performance_bar/services/performance_bar_service.js +++ b/app/assets/javascripts/performance_bar/services/performance_bar_service.js @@ -10,29 +10,25 @@ export default class PerformanceBarService { } static registerInterceptor(peekUrl, callback) { - vueResourceInterceptor = (request, next) => { - next(response => { - const requestId = response.headers['x-request-id']; - const requestUrl = response.url; - - if (requestUrl !== peekUrl && requestId) { - callback(requestId, requestUrl); - } - }); - }; - - Vue.http.interceptors.push(vueResourceInterceptor); - - return axios.interceptors.response.use(response => { + const interceptor = response => { const requestId = response.headers['x-request-id']; - const requestUrl = response.config.url; + // Get the request URL from response.config for Axios, and response for + // Vue Resource. + const requestUrl = (response.config || response).url; + const cachedResponse = response.headers['x-gitlab-from-cache'] === 'true'; - if (requestUrl !== peekUrl && requestId) { + if (requestUrl !== peekUrl && requestId && !cachedResponse) { callback(requestId, requestUrl); } return response; - }); + }; + + vueResourceInterceptor = (request, next) => next(interceptor); + + Vue.http.interceptors.push(vueResourceInterceptor); + + return axios.interceptors.response.use(interceptor); } static removeInterceptor(interceptor) { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js deleted file mode 100644 index 4d9a2ca530f..00000000000 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js +++ /dev/null @@ -1,18 +0,0 @@ -import statusIcon from '../mr_widget_status_icon.vue'; - -export default { - name: 'MRWidgetPipelineBlocked', - components: { - statusIcon, - }, - template: ` - <div class="mr-widget-body media"> - <status-icon status="warning" :show-disabled-button="true" /> - <div class="media-body space-children"> - <span class="bold"> - The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure - </span> - </div> - </div> - `, -}; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue new file mode 100644 index 00000000000..8d55477929f --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue @@ -0,0 +1,25 @@ +<script> +import statusIcon from '../mr_widget_status_icon.vue'; + +export default { + name: 'PipelineFailed', + components: { + statusIcon, + }, +}; +</script> + +<template> + <div class="mr-widget-body media"> + <status-icon + status="warning" + :show-disabled-button="true" + /> + <div class="media-body space-children"> + <span class="bold"> + {{ s__(`mrWidget|The pipeline for this merge request failed. +Please retry the job or push a new commit to fix the failure`) }} + </span> + </div> + </div> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js index 86d52f89d38..3b5c973e4a0 100644 --- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js +++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js @@ -31,7 +31,7 @@ export { default as ReadyToMergeState } from './components/states/ready_to_merge export { default as ShaMismatchState } from './components/states/sha_mismatch.vue'; export { default as UnresolvedDiscussionsState } from './components/states/unresolved_discussions.vue'; export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue'; -export { default as PipelineFailedState } from './components/states/mr_widget_pipeline_failed'; +export { default as PipelineFailedState } from './components/states/pipeline_failed.vue'; export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue'; export { default as RebaseState } from './components/states/mr_widget_rebase.vue'; export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue'; diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index 97789636787..b8875d04488 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -175,7 +175,7 @@ </a> </span> <span v-else> - Cant find HEAD commit for this branch + Can't find HEAD commit for this branch </span> </div> </div> |