debian-mirror-gitlab/app/assets/javascripts/issue_show/components/app.vue

350 lines
8.7 KiB
Vue
Raw Normal View History

2017-09-10 17:25:29 +05:30
<script>
2018-12-13 13:39:08 +05:30
import Visibility from 'visibilityjs';
2019-03-02 22:35:43 +05:30
import { __, s__, sprintf } from '~/locale';
import createFlash from '~/flash';
2018-12-13 13:39:08 +05:30
import { visitUrl } from '../../lib/utils/url_utility';
import Poll from '../../lib/utils/poll';
import eventHub from '../event_hub';
import Service from '../services/index';
import Store from '../stores';
import titleComponent from './title.vue';
import descriptionComponent from './description.vue';
import editedComponent from './edited.vue';
import formComponent from './form.vue';
import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
2017-09-10 17:25:29 +05:30
2018-12-13 13:39:08 +05:30
export default {
components: {
descriptionComponent,
titleComponent,
editedComponent,
formComponent,
},
mixins: [recaptchaModalImplementor],
props: {
endpoint: {
required: true,
type: String,
2017-09-10 17:25:29 +05:30
},
2018-12-13 13:39:08 +05:30
updateEndpoint: {
required: true,
type: String,
},
canUpdate: {
required: true,
type: Boolean,
},
canDestroy: {
required: true,
type: Boolean,
},
showInlineEditButton: {
type: Boolean,
required: false,
default: true,
},
showDeleteButton: {
type: Boolean,
required: false,
default: true,
},
enableAutocomplete: {
type: Boolean,
required: false,
default: true,
},
issuableRef: {
type: String,
required: true,
},
initialTitleHtml: {
type: String,
required: true,
},
initialTitleText: {
type: String,
required: true,
},
initialDescriptionHtml: {
type: String,
required: false,
default: '',
},
initialDescriptionText: {
type: String,
required: false,
default: '',
},
initialTaskStatus: {
type: String,
required: false,
default: '',
},
updatedAt: {
type: String,
required: false,
default: '',
},
updatedByName: {
type: String,
required: false,
default: '',
},
updatedByPath: {
type: String,
required: false,
default: '',
},
issuableTemplates: {
type: Array,
required: false,
default: () => [],
},
markdownPreviewPath: {
type: String,
required: true,
},
markdownDocsPath: {
type: String,
required: true,
},
projectPath: {
type: String,
required: true,
},
projectNamespace: {
type: String,
required: true,
},
issuableType: {
type: String,
required: false,
default: 'issue',
},
canAttachFile: {
type: Boolean,
required: false,
default: true,
},
2019-03-02 22:35:43 +05:30
lockVersion: {
type: Number,
required: false,
default: 0,
},
2018-12-13 13:39:08 +05:30
},
data() {
const store = new Store({
titleHtml: this.initialTitleHtml,
titleText: this.initialTitleText,
descriptionHtml: this.initialDescriptionHtml,
descriptionText: this.initialDescriptionText,
updatedAt: this.updatedAt,
updatedByName: this.updatedByName,
updatedByPath: this.updatedByPath,
taskStatus: this.initialTaskStatus,
2019-03-02 22:35:43 +05:30
lock_version: this.lockVersion,
2018-12-13 13:39:08 +05:30
});
2017-09-10 17:25:29 +05:30
2018-12-13 13:39:08 +05:30
return {
store,
state: store.state,
showForm: false,
};
},
computed: {
formState() {
return this.store.formState;
2017-09-10 17:25:29 +05:30
},
2018-12-13 13:39:08 +05:30
hasUpdated() {
return !!this.state.updatedAt;
},
issueChanged() {
const descriptionChanged = this.initialDescriptionText !== this.store.formState.description;
const titleChanged = this.initialTitleText !== this.store.formState.title;
return descriptionChanged || titleChanged;
},
2019-03-02 22:35:43 +05:30
defaultErrorMessage() {
return sprintf(s__('Error updating %{issuableType}'), { issuableType: this.issuableType });
},
2018-12-13 13:39:08 +05:30
},
created() {
this.service = new Service(this.endpoint);
this.poll = new Poll({
resource: this.service,
method: 'getData',
successCallback: res => this.store.updateState(res.data),
errorCallback(err) {
throw new Error(err);
},
});
if (!Visibility.hidden()) {
this.poll.makeRequest();
}
2018-03-17 18:26:18 +05:30
2018-12-13 13:39:08 +05:30
Visibility.change(() => {
2018-03-17 18:26:18 +05:30
if (!Visibility.hidden()) {
2018-12-13 13:39:08 +05:30
this.poll.restart();
} else {
this.poll.stop();
2017-09-10 17:25:29 +05:30
}
2018-12-13 13:39:08 +05:30
});
2018-03-17 18:26:18 +05:30
2018-12-13 13:39:08 +05:30
window.addEventListener('beforeunload', this.handleBeforeUnloadEvent);
2018-03-17 18:26:18 +05:30
2018-12-13 13:39:08 +05:30
eventHub.$on('delete.issuable', this.deleteIssuable);
eventHub.$on('update.issuable', this.updateIssuable);
eventHub.$on('close.form', this.closeForm);
eventHub.$on('open.form', this.openForm);
},
beforeDestroy() {
eventHub.$off('delete.issuable', this.deleteIssuable);
eventHub.$off('update.issuable', this.updateIssuable);
eventHub.$off('close.form', this.closeForm);
eventHub.$off('open.form', this.openForm);
window.removeEventListener('beforeunload', this.handleBeforeUnloadEvent);
},
methods: {
handleBeforeUnloadEvent(e) {
const event = e;
2019-03-02 22:35:43 +05:30
if (this.showForm && this.issueChanged && !this.showRecaptcha) {
event.returnValue = __('Are you sure you want to lose your issue information?');
2018-12-13 13:39:08 +05:30
}
return undefined;
},
2019-03-02 22:35:43 +05:30
updateStoreState() {
return this.service
.getData()
.then(res => res.data)
.then(data => {
this.store.updateState(data);
})
.catch(() => {
createFlash(this.defaultErrorMessage);
});
},
2017-09-10 17:25:29 +05:30
2018-12-13 13:39:08 +05:30
openForm() {
if (!this.showForm) {
this.showForm = true;
this.store.setFormState({
title: this.state.titleText,
description: this.state.descriptionText,
2019-03-02 22:35:43 +05:30
lock_version: this.state.lock_version,
2018-12-13 13:39:08 +05:30
lockedWarningVisible: false,
updateLoading: false,
});
}
},
closeForm() {
this.showForm = false;
},
2017-09-10 17:25:29 +05:30
2018-12-13 13:39:08 +05:30
updateIssuable() {
return this.service
.updateIssuable(this.store.formState)
.then(res => res.data)
.then(data => this.checkForSpam(data))
.then(data => {
if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url);
}
})
2019-03-02 22:35:43 +05:30
.then(this.updateStoreState)
.then(() => {
2018-12-13 13:39:08 +05:30
eventHub.$emit('close.form');
})
2019-03-02 22:35:43 +05:30
.catch((error = {}) => {
const { name, response = {} } = error;
if (name === 'SpamError') {
2018-12-13 13:39:08 +05:30
this.openRecaptcha();
} else {
2019-03-02 22:35:43 +05:30
let errMsg = this.defaultErrorMessage;
if (response.data && response.data.errors) {
errMsg += `. ${response.data.errors.join(' ')}`;
}
createFlash(errMsg);
2018-12-13 13:39:08 +05:30
}
2017-09-10 17:25:29 +05:30
});
2018-12-13 13:39:08 +05:30
},
2017-09-10 17:25:29 +05:30
2018-12-13 13:39:08 +05:30
closeRecaptchaModal() {
this.store.setFormState({
updateLoading: false,
});
2017-09-10 17:25:29 +05:30
2018-12-13 13:39:08 +05:30
this.closeRecaptcha();
},
2017-09-10 17:25:29 +05:30
2018-12-13 13:39:08 +05:30
deleteIssuable() {
this.service
.deleteIssuable()
.then(res => res.data)
.then(data => {
// Stop the poll so we don't get 404's with the issuable not existing
this.poll.stop();
visitUrl(data.web_url);
})
.catch(() => {
2019-03-02 22:35:43 +05:30
createFlash(
sprintf(s__('Error deleting %{issuableType}'), { issuableType: this.issuableType }),
);
2018-12-13 13:39:08 +05:30
});
2018-03-17 18:26:18 +05:30
},
2018-12-13 13:39:08 +05:30
},
};
2017-09-10 17:25:29 +05:30
</script>
<template>
<div>
2018-03-17 18:26:18 +05:30
<div v-if="canUpdate && showForm">
<form-component
:form-state="formState"
:can-destroy="canDestroy"
:issuable-templates="issuableTemplates"
:markdown-docs-path="markdownDocsPath"
:markdown-preview-path="markdownPreviewPath"
:project-path="projectPath"
:project-namespace="projectNamespace"
:show-delete-button="showDeleteButton"
:can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete"
2018-12-05 23:21:45 +05:30
:issuable-type="issuableType"
2018-03-17 18:26:18 +05:30
/>
2019-02-15 15:39:39 +05:30
<recaptcha-modal v-show="showRecaptcha" :html="recaptchaHTML" @close="closeRecaptchaModal" />
2018-03-17 18:26:18 +05:30
</div>
2017-09-10 17:25:29 +05:30
<div v-else>
<title-component
:issuable-ref="issuableRef"
2018-03-17 18:26:18 +05:30
:can-update="canUpdate"
2017-09-10 17:25:29 +05:30
:title-html="state.titleHtml"
2018-03-17 18:26:18 +05:30
:title-text="state.titleText"
:show-inline-edit-button="showInlineEditButton"
/>
2017-09-10 17:25:29 +05:30
<description-component
v-if="state.descriptionHtml"
:can-update="canUpdate"
:description-html="state.descriptionHtml"
:description-text="state.descriptionText"
:updated-at="state.updatedAt"
2018-03-17 18:26:18 +05:30
:task-status="state.taskStatus"
:issuable-type="issuableType"
:update-url="updateEndpoint"
2019-03-02 22:35:43 +05:30
:lock-version="state.lock_version"
@taskListUpdateFailed="updateStoreState"
2018-03-17 18:26:18 +05:30
/>
2017-09-10 17:25:29 +05:30
<edited-component
v-if="hasUpdated"
:updated-at="state.updatedAt"
:updated-by-name="state.updatedByName"
2018-03-17 18:26:18 +05:30
:updated-by-path="state.updatedByPath"
/>
2017-09-10 17:25:29 +05:30
</div>
</div>
</template>