Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Knox <psimyn@gmail.com>2017-12-08 23:24:52 +0300
committerSimon Knox <psimyn@gmail.com>2017-12-08 23:24:52 +0300
commit874deb414263de260479c135e58e23a5497c91fc (patch)
tree2760e05e25fe855dc0f6dc404da43a921fe457e3 /app/assets/javascripts/notes/components/comment_form.vue
parentd32032de08585476c3b303803a307ff94d910ebd (diff)
rename remaining issue_ prefixed files
Diffstat (limited to 'app/assets/javascripts/notes/components/comment_form.vue')
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue381
1 files changed, 381 insertions, 0 deletions
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
new file mode 100644
index 00000000000..e594377bc40
--- /dev/null
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -0,0 +1,381 @@
+<script>
+ import { mapActions, mapGetters } from 'vuex';
+ import _ from 'underscore';
+ import Autosize from 'autosize';
+ import Flash from '../../flash';
+ import Autosave from '../../autosave';
+ import TaskList from '../../task_list';
+ import * as constants from '../constants';
+ import eventHub from '../event_hub';
+ import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
+ import noteSignedOutWidget from './note_signed_out_widget.vue';
+ import discussionLockedWidget from './discussion_locked_widget.vue';
+ import markdownField from '../../vue_shared/components/markdown/field.vue';
+ import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
+ import issuableStateMixin from '../mixins/issuable_state';
+
+ export default {
+ name: 'commentForm',
+ data() {
+ return {
+ note: '',
+ noteType: constants.COMMENT,
+ // Can't use mapGetters,
+ // this needs to be in the data object because it belongs to the state
+ issueState: this.$store.getters.getNoteableData.state,
+ isSubmitting: false,
+ isSubmitButtonDisabled: true,
+ };
+ },
+ components: {
+ issueWarning,
+ noteSignedOutWidget,
+ discussionLockedWidget,
+ markdownField,
+ userAvatarLink,
+ },
+ watch: {
+ note(newNote) {
+ this.setIsSubmitButtonDisabled(newNote, this.isSubmitting);
+ },
+ isSubmitting(newValue) {
+ this.setIsSubmitButtonDisabled(this.note, newValue);
+ },
+ },
+ computed: {
+ ...mapGetters([
+ 'getCurrentUserLastNote',
+ 'getUserData',
+ 'getNoteableData',
+ 'getNotesData',
+ ]),
+ isLoggedIn() {
+ return this.getUserData.id;
+ },
+ commentButtonTitle() {
+ return this.noteType === constants.COMMENT ? 'Comment' : 'Start discussion';
+ },
+ isIssueOpen() {
+ return this.issueState === constants.OPENED || this.issueState === constants.REOPENED;
+ },
+ canCreateNote() {
+ return this.getNoteableData.current_user.can_create_note;
+ },
+ issueActionButtonTitle() {
+ if (this.note.length) {
+ const actionText = this.isIssueOpen ? 'close' : 'reopen';
+
+ return this.noteType === constants.COMMENT ? `Comment & ${actionText} issue` : `Start discussion & ${actionText} issue`;
+ }
+
+ return this.isIssueOpen ? 'Close issue' : 'Reopen issue';
+ },
+ actionButtonClassNames() {
+ return {
+ 'btn-reopen': !this.isIssueOpen,
+ 'btn-close': this.isIssueOpen,
+ 'js-note-target-close': this.isIssueOpen,
+ 'js-note-target-reopen': !this.isIssueOpen,
+ };
+ },
+ markdownDocsPath() {
+ return this.getNotesData.markdownDocsPath;
+ },
+ quickActionsDocsPath() {
+ return this.getNotesData.quickActionsDocsPath;
+ },
+ markdownPreviewPath() {
+ return this.getNoteableData.preview_note_path;
+ },
+ author() {
+ return this.getUserData;
+ },
+ canUpdateIssue() {
+ return this.getNoteableData.current_user.can_update;
+ },
+ endpoint() {
+ return this.getNoteableData.create_note_path;
+ },
+ },
+ methods: {
+ ...mapActions([
+ 'saveNote',
+ 'stopPolling',
+ 'restartPolling',
+ 'removePlaceholderNotes',
+ ]),
+ setIsSubmitButtonDisabled(note, isSubmitting) {
+ if (!_.isEmpty(note) && !isSubmitting) {
+ this.isSubmitButtonDisabled = false;
+ } else {
+ this.isSubmitButtonDisabled = true;
+ }
+ },
+ handleSave(withIssueAction) {
+ if (this.note.length) {
+ const noteData = {
+ endpoint: this.endpoint,
+ flashContainer: this.$el,
+ data: {
+ note: {
+ noteable_type: constants.NOTEABLE_TYPE,
+ noteable_id: this.getNoteableData.id,
+ note: this.note,
+ },
+ },
+ };
+
+ if (this.noteType === constants.DISCUSSION) {
+ noteData.data.note.type = constants.DISCUSSION_NOTE;
+ }
+ this.isSubmitting = true;
+ this.note = ''; // Empty textarea while being requested. Repopulate in catch
+ this.resizeTextarea();
+ this.stopPolling();
+
+ this.saveNote(noteData)
+ .then((res) => {
+ this.isSubmitting = false;
+ this.restartPolling();
+
+ if (res.errors) {
+ if (res.errors.commands_only) {
+ this.discard();
+ } else {
+ Flash(
+ 'Something went wrong while adding your comment. Please try again.',
+ 'alert',
+ this.$refs.commentForm,
+ );
+ }
+ } else {
+ this.discard();
+ }
+
+ if (withIssueAction) {
+ this.toggleIssueState();
+ }
+ })
+ .catch(() => {
+ this.isSubmitting = false;
+ this.discard(false);
+ const msg = 'Your comment could not be submitted! Please check your network connection and try again.';
+ Flash(msg, 'alert', this.$el);
+ this.note = noteData.data.note.note; // Restore textarea content.
+ this.removePlaceholderNotes();
+ });
+ } else {
+ this.toggleIssueState();
+ }
+ },
+ toggleIssueState() {
+ this.issueState = this.isIssueOpen ? constants.CLOSED : constants.REOPENED;
+
+ // This is out of scope for the Notes Vue component.
+ // It was the shortest path to update the issue state and relevant places.
+ const btnClass = this.isIssueOpen ? 'btn-reopen' : 'btn-close';
+ $(`.js-btn-issue-action.${btnClass}:visible`).trigger('click');
+ },
+ discard(shouldClear = true) {
+ // `blur` is needed to clear slash commands autocomplete cache if event fired.
+ // `focus` is needed to remain cursor in the textarea.
+ this.$refs.textarea.blur();
+ this.$refs.textarea.focus();
+
+ if (shouldClear) {
+ this.note = '';
+ this.resizeTextarea();
+ this.$refs.markdownField.previewMarkdown = false;
+ }
+
+ // reset autostave
+ this.autosave.reset();
+ },
+ setNoteType(type) {
+ this.noteType = type;
+ },
+ editCurrentUserLastNote() {
+ if (this.note === '') {
+ const lastNote = this.getCurrentUserLastNote;
+
+ if (lastNote) {
+ eventHub.$emit('enterEditMode', {
+ noteId: lastNote.id,
+ });
+ }
+ }
+ },
+ initAutoSave() {
+ if (this.isLoggedIn) {
+ this.autosave = new Autosave($(this.$refs.textarea), ['Note', 'Issue', this.getNoteableData.id], 'issue');
+ }
+ },
+ initTaskList() {
+ return new TaskList({
+ dataType: 'note',
+ fieldName: 'note',
+ selector: '.notes',
+ });
+ },
+ resizeTextarea() {
+ this.$nextTick(() => {
+ Autosize.update(this.$refs.textarea);
+ });
+ },
+ },
+ mixins: [
+ issuableStateMixin,
+ ],
+ mounted() {
+ // jQuery is needed here because it is a custom event being dispatched with jQuery.
+ $(document).on('issuable:change', (e, isClosed) => {
+ this.issueState = isClosed ? constants.CLOSED : constants.REOPENED;
+ });
+
+ this.initAutoSave();
+ this.initTaskList();
+ },
+ };
+</script>
+
+<template>
+ <div>
+ <note-signed-out-widget v-if="!isLoggedIn" />
+ <discussion-locked-widget
+ issuable-type="issue"
+ v-else-if="!canCreateNote"
+ />
+ <ul
+ v-else
+ class="notes notes-form timeline">
+ <li class="timeline-entry">
+ <div class="timeline-entry-inner">
+ <div class="flash-container error-alert timeline-content"></div>
+ <div class="timeline-icon hidden-xs hidden-sm">
+ <user-avatar-link
+ v-if="author"
+ :link-href="author.path"
+ :img-src="author.avatar_url"
+ :img-alt="author.name"
+ :img-size="40"
+ />
+ </div>
+ <div class="timeline-content timeline-content-form">
+ <form
+ ref="commentForm"
+ class="new-note js-quick-submit common-note-form gfm-form js-main-target-form"
+ >
+
+ <div class="error-alert"></div>
+
+ <issue-warning
+ v-if="hasWarning(getNoteableData)"
+ :is-locked="isLocked(getNoteableData)"
+ :is-confidential="isConfidential(getNoteableData)"
+ />
+
+ <markdown-field
+ :markdown-preview-path="markdownPreviewPath"
+ :markdown-docs-path="markdownDocsPath"
+ :quick-actions-docs-path="quickActionsDocsPath"
+ :add-spacing-classes="false"
+ ref="markdownField">
+ <textarea
+ id="note-body"
+ name="note[note]"
+ class="note-textarea js-vue-comment-form js-gfm-input js-autosize markdown-area js-vue-textarea"
+ data-supports-quick-actions="true"
+ aria-label="Description"
+ v-model="note"
+ ref="textarea"
+ slot="textarea"
+ :disabled="isSubmitting"
+ placeholder="Write a comment or drag your files here..."
+ @keydown.up="editCurrentUserLastNote()"
+ @keydown.meta.enter="handleSave()">
+ </textarea>
+ </markdown-field>
+ <div class="note-form-actions">
+ <div class="pull-left btn-group append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown">
+ <button
+ @click.prevent="handleSave()"
+ :disabled="isSubmitButtonDisabled"
+ class="btn btn-create comment-btn js-comment-button js-comment-submit-button"
+ type="submit">
+ {{commentButtonTitle}}
+ </button>
+ <button
+ :disabled="isSubmitButtonDisabled"
+ name="button"
+ type="button"
+ class="btn comment-btn note-type-toggle js-note-new-discussion dropdown-toggle"
+ data-toggle="dropdown"
+ aria-label="Open comment type dropdown">
+ <i
+ aria-hidden="true"
+ class="fa fa-caret-down toggle-icon">
+ </i>
+ </button>
+
+ <ul class="note-type-dropdown dropdown-open-top dropdown-menu">
+ <li :class="{ 'droplab-item-selected': noteType === 'comment' }">
+ <button
+ type="button"
+ class="btn btn-transparent"
+ @click.prevent="setNoteType('comment')">
+ <i
+ aria-hidden="true"
+ class="fa fa-check icon">
+ </i>
+ <div class="description">
+ <strong>Comment</strong>
+ <p>
+ Add a general comment to this issue.
+ </p>
+ </div>
+ </button>
+ </li>
+ <li class="divider droplab-item-ignore"></li>
+ <li :class="{ 'droplab-item-selected': noteType === 'discussion' }">
+ <button
+ type="button"
+ class="btn btn-transparent"
+ @click.prevent="setNoteType('discussion')">
+ <i
+ aria-hidden="true"
+ class="fa fa-check icon">
+ </i>
+ <div class="description">
+ <strong>Start discussion</strong>
+ <p>
+ Discuss a specific suggestion or question.
+ </p>
+ </div>
+ </button>
+ </li>
+ </ul>
+ </div>
+ <button
+ type="button"
+ @click="handleSave(true)"
+ v-if="canUpdateIssue"
+ :class="actionButtonClassNames"
+ :disabled="isSubmitting"
+ class="btn btn-comment btn-comment-and-close js-action-button">
+ {{issueActionButtonTitle}}
+ </button>
+ <button
+ type="button"
+ v-if="note.length"
+ @click="discard"
+ class="btn btn-cancel js-note-discard">
+ Discard draft
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+</template>