2017-09-10 17:25:29 +05:30
|
|
|
<script>
|
2018-03-17 18:26:18 +05:30
|
|
|
import Visibility from 'visibilityjs';
|
|
|
|
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-03-17 18:26:18 +05:30
|
|
|
export default {
|
|
|
|
components: {
|
|
|
|
descriptionComponent,
|
|
|
|
titleComponent,
|
|
|
|
editedComponent,
|
|
|
|
formComponent,
|
2017-09-10 17:25:29 +05:30
|
|
|
},
|
2018-03-17 18:26:18 +05:30
|
|
|
mixins: [
|
|
|
|
recaptchaModalImplementor,
|
|
|
|
],
|
|
|
|
props: {
|
|
|
|
endpoint: {
|
|
|
|
required: true,
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
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,
|
|
|
|
},
|
2017-09-10 17:25:29 +05:30
|
|
|
},
|
2018-03-17 18:26:18 +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,
|
|
|
|
});
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
return {
|
|
|
|
store,
|
|
|
|
state: store.state,
|
|
|
|
showForm: false,
|
|
|
|
};
|
2017-09-10 17:25:29 +05:30
|
|
|
},
|
2018-03-17 18:26:18 +05:30
|
|
|
computed: {
|
|
|
|
formState() {
|
|
|
|
return this.store.formState;
|
|
|
|
},
|
|
|
|
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;
|
|
|
|
},
|
2017-09-10 17:25:29 +05:30
|
|
|
},
|
2018-03-17 18:26:18 +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();
|
2017-09-10 17:25:29 +05:30
|
|
|
}
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
Visibility.change(() => {
|
|
|
|
if (!Visibility.hidden()) {
|
|
|
|
this.poll.restart();
|
|
|
|
} else {
|
|
|
|
this.poll.stop();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
window.addEventListener('beforeunload', this.handleBeforeUnloadEvent);
|
|
|
|
|
|
|
|
eventHub.$on('delete.issuable', this.deleteIssuable);
|
|
|
|
eventHub.$on('update.issuable', this.updateIssuable);
|
|
|
|
eventHub.$on('close.form', this.closeForm);
|
|
|
|
eventHub.$on('open.form', this.openForm);
|
2017-09-10 17:25:29 +05:30
|
|
|
},
|
2018-03-17 18:26:18 +05:30
|
|
|
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);
|
2017-09-10 17:25:29 +05:30
|
|
|
},
|
2018-03-17 18:26:18 +05:30
|
|
|
methods: {
|
|
|
|
handleBeforeUnloadEvent(e) {
|
|
|
|
const event = e;
|
|
|
|
if (this.showForm && this.issueChanged) {
|
|
|
|
event.returnValue = 'Are you sure you want to lose your issue information?';
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
},
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
openForm() {
|
|
|
|
if (!this.showForm) {
|
|
|
|
this.showForm = true;
|
|
|
|
this.store.setFormState({
|
|
|
|
title: this.state.titleText,
|
|
|
|
description: this.state.descriptionText,
|
|
|
|
lockedWarningVisible: false,
|
|
|
|
updateLoading: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
closeForm() {
|
|
|
|
this.showForm = false;
|
|
|
|
},
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
updateIssuable() {
|
|
|
|
return this.service.updateIssuable(this.store.formState)
|
|
|
|
.then(res => res.data)
|
|
|
|
.then(data => this.checkForSpam(data))
|
|
|
|
.then((data) => {
|
|
|
|
if (location.pathname !== data.web_url) {
|
|
|
|
visitUrl(data.web_url);
|
|
|
|
}
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
return this.service.getData();
|
|
|
|
})
|
|
|
|
.then(res => res.data)
|
|
|
|
.then((data) => {
|
|
|
|
this.store.updateState(data);
|
|
|
|
eventHub.$emit('close.form');
|
|
|
|
})
|
|
|
|
.catch((error) => {
|
|
|
|
if (error && error.name === 'SpamError') {
|
|
|
|
this.openRecaptcha();
|
|
|
|
} else {
|
|
|
|
eventHub.$emit('close.form');
|
|
|
|
window.Flash(`Error updating ${this.issuableType}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
closeRecaptchaModal() {
|
|
|
|
this.store.setFormState({
|
|
|
|
updateLoading: false,
|
2017-09-10 17:25:29 +05:30
|
|
|
});
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
this.closeRecaptcha();
|
|
|
|
},
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-03-17 18:26:18 +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();
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
visitUrl(data.web_url);
|
|
|
|
})
|
|
|
|
.catch(() => {
|
|
|
|
eventHub.$emit('close.form');
|
|
|
|
window.Flash(`Error deleting ${this.issuableType}`);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
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"
|
|
|
|
/>
|
|
|
|
|
|
|
|
<recaptcha-modal
|
|
|
|
v-show="showRecaptcha"
|
|
|
|
:html="recaptchaHTML"
|
|
|
|
@close="closeRecaptchaModal"
|
|
|
|
/>
|
|
|
|
</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"
|
|
|
|
/>
|
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>
|