summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-07-26 12:02:01 +0100
committerFilipa Lacerda <filipa@gitlab.com>2017-07-26 12:02:01 +0100
commitffef16690c61581e3254dd074e55b58dc4d2b7bb (patch)
tree4cd9a6de5b1513d4d71a5eb290a4bafd17c743b5
parent4e81ad2ab9eb0190527514cd8b7cef44df4a9bfc (diff)
downloadgitlab-ce-ffef16690c61581e3254dd074e55b58dc4d2b7bb.tar.gz
Use mapActions, mapGetters and mapMutations for components
-rw-r--r--app/assets/javascripts/notes/components/issue_comment_form.vue6
-rw-r--r--app/assets/javascripts/notes/components/issue_discussion.vue196
-rw-r--r--app/assets/javascripts/notes/components/issue_note.vue208
-rw-r--r--app/assets/javascripts/notes/components/issue_note_actions.vue133
-rw-r--r--app/assets/javascripts/notes/components/issue_note_awards_list.vue315
-rw-r--r--app/assets/javascripts/notes/components/issue_note_body.vue118
-rw-r--r--app/assets/javascripts/notes/components/issue_note_edited_text.vue49
-rw-r--r--app/assets/javascripts/notes/components/issue_note_form.vue148
-rw-r--r--app/assets/javascripts/notes/components/issue_note_header.vue126
-rw-r--r--app/assets/javascripts/notes/components/issue_note_signed_out_widget.vue26
-rw-r--r--app/assets/javascripts/notes/components/issue_notes.vue225
-rw-r--r--app/assets/javascripts/notes/components/issue_placeholder_note.vue27
-rw-r--r--app/assets/javascripts/notes/components/issue_placeholder_system_note.vue14
-rw-r--r--app/assets/javascripts/notes/components/issue_system_note.vue54
-rw-r--r--app/assets/javascripts/notes/constants.js2
-rw-r--r--app/assets/javascripts/notes/stores/actions.js7
16 files changed, 850 insertions, 804 deletions
diff --git a/app/assets/javascripts/notes/components/issue_comment_form.vue b/app/assets/javascripts/notes/components/issue_comment_form.vue
index c913f3f5fad..bd6caddb40f 100644
--- a/app/assets/javascripts/notes/components/issue_comment_form.vue
+++ b/app/assets/javascripts/notes/components/issue_comment_form.vue
@@ -1,6 +1,7 @@
<script>
/* global Flash */
+ import { mapActions } from 'vuex';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import markdownField from '../../vue_shared/components/markdown/field.vue';
import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
@@ -60,6 +61,9 @@
},
},
methods: {
+ ...mapActions([
+ 'saveNote'
+ ]),
handleSave(withIssueAction) {
if (this.note.length) {
const noteData = {
@@ -79,7 +83,7 @@
noteData.data.note.type = constants.DISCUSSION_NOTE;
}
- this.$store.dispatch('saveNote', noteData)
+ this.saveNote(noteData)
.then((res) => {
if (res.errors) {
if (res.errors.commands_only) {
diff --git a/app/assets/javascripts/notes/components/issue_discussion.vue b/app/assets/javascripts/notes/components/issue_discussion.vue
index 7fc4a41fa8a..21b5b288121 100644
--- a/app/assets/javascripts/notes/components/issue_discussion.vue
+++ b/app/assets/javascripts/notes/components/issue_discussion.vue
@@ -1,108 +1,112 @@
<script>
-/* global Flash */
+ /* global Flash */
+ import { mapActions } from 'vuex';
+ import { TOGGLE_DISCUSSION } from '../stores/mutation_types';
+ import { SYSTEM_NOTE } from '../constants';
+ import issueNote from './issue_note.vue';
+ import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
+ import issueNoteHeader from './issue_note_header.vue';
+ import issueNoteActions from './issue_note_actions.vue';
+ import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
+ import issueNoteEditedText from './issue_note_edited_text.vue';
+ import issueNoteForm from './issue_note_form.vue';
+ import placeholderNote from './issue_placeholder_note.vue';
+ import placeholderSystemNote from './issue_placeholder_system_note.vue';
-import issueNote from './issue_note.vue';
-import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
-import issueNoteHeader from './issue_note_header.vue';
-import issueNoteActions from './issue_note_actions.vue';
-import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
-import issueNoteEditedText from './issue_note_edited_text.vue';
-import issueNoteForm from './issue_note_form.vue';
-import placeholderNote from './issue_placeholder_note.vue';
-import placeholderSystemNote from './issue_placeholder_system_note.vue';
-
-export default {
- props: {
- note: {
- type: Object,
- required: true,
+ export default {
+ props: {
+ note: {
+ type: Object,
+ required: true,
+ },
},
- },
- data() {
- return {
- newNotePath: window.gl.issueData.create_note_path,
- isReplying: false,
- };
- },
- components: {
- issueNote,
- userAvatarLink,
- issueNoteHeader,
- issueNoteActions,
- issueNoteSignedOutWidget,
- issueNoteEditedText,
- issueNoteForm,
- placeholderNote,
- placeholderSystemNote,
- },
- computed: {
- discussion() {
- return this.note.notes[0];
+ data() {
+ return {
+ newNotePath: window.gl.issueData.create_note_path,
+ isReplying: false,
+ };
},
- author() {
- return this.discussion.author;
+ components: {
+ issueNote,
+ userAvatarLink,
+ issueNoteHeader,
+ issueNoteActions,
+ issueNoteSignedOutWidget,
+ issueNoteEditedText,
+ issueNoteForm,
+ placeholderNote,
+ placeholderSystemNote,
},
- canReply() {
- return window.gl.issueData.current_user.can_create_note;
+ computed: {
+ discussion() {
+ return this.note.notes[0];
+ },
+ author() {
+ return this.discussion.author;
+ },
+ canReply() {
+ return window.gl.issueData.current_user.can_create_note;
+ },
},
- },
- methods: {
- componentName(note) {
- if (note.isPlaceholderNote) {
- if (note.placeholderType === 'systemNote') {
- return placeholderSystemNote;
+ methods: {
+ ...mapActions([
+ 'saveNote',
+ ]),
+ ...mapMutations({
+ toggleDiscussion: TOGGLE_DISCUSSION,
+ }),
+ componentName(note) {
+ if (note.isPlaceholderNote) {
+ if (note.placeholderType === SYSTEM_NOTE) {
+ return placeholderSystemNote;
+ }
+ return placeholderNote;
}
- return placeholderNote;
- }
- return issueNote;
- },
- componentData(note) {
- return note.isPlaceholderNote ? note.notes[0] : note;
- },
- toggleDiscussion() {
- this.$store.commit('toggleDiscussion', {
- discussionId: this.note.id,
- });
- },
- showReplyForm() {
- this.isReplying = true;
- },
- cancelReplyForm(shouldConfirm) {
- if (shouldConfirm && this.$refs.noteForm.isDirty) {
- const msg = 'Are you sure you want to cancel creating this comment?';
- // eslint-disable-next-line no-alert
- const isConfirmed = confirm(msg);
- if (!isConfirmed) {
- return;
+ return issueNote;
+ },
+ componentData(note) {
+ return note.isPlaceholderNote ? note.notes[0] : note;
+ },
+ toggleDiscussion() {
+ this.toggleDiscussion({ discussionId: this.note.id });
+ },
+ showReplyForm() {
+ this.isReplying = true;
+ },
+ cancelReplyForm(shouldConfirm) {
+ if (shouldConfirm && this.$refs.noteForm.isDirty) {
+ const msg = 'Are you sure you want to cancel creating this comment?';
+ // eslint-disable-next-line no-alert
+ const isConfirmed = confirm(msg);
+ if (!isConfirmed) {
+ return;
+ }
}
- }
- this.isReplying = false;
- },
- saveReply({ note }) {
- const replyData = {
- endpoint: this.newNotePath,
- flashContainer: this.$el,
- data: {
- in_reply_to_discussion_id: this.note.reply_id,
- target_type: 'issue',
- target_id: this.discussion.noteable_id,
- note: { note },
- full_data: true,
- },
- };
+ this.isReplying = false;
+ },
+ saveReply({ note }) {
+ const replyData = {
+ endpoint: this.newNotePath,
+ flashContainer: this.$el,
+ data: {
+ in_reply_to_discussion_id: this.note.reply_id,
+ target_type: 'issue',
+ target_id: this.discussion.noteable_id,
+ note: { note },
+ full_data: true,
+ },
+ };
- this.$store.dispatch('saveNote', replyData)
- .then(() => {
- this.isReplying = false;
- })
- .catch(() => {
- Flash('Something went wrong while adding your reply. Please try again.');
- });
+ this.saveNote(replyData)
+ .then(() => {
+ this.isReplying = false;
+ })
+ .catch(() => Flash('Something went wrong while adding your reply. Please try again.'));
+ },
},
- },
-};
+ };
</script>
<template>
@@ -132,8 +136,7 @@ export default {
:edited-at="note.last_updated_at"
:edited-by="note.last_updated_by"
actionText="Last updated"
- className="discussion-headline-light js-discussion-headline"
- />
+ className="discussion-headline-light js-discussion-headline" />
</div>
</div>
<div
@@ -162,7 +165,8 @@ export default {
saveButtonTitle="Comment"
:update-handler="saveReply"
:cancel-handler="cancelReplyForm"
- ref="noteForm" />
+ ref="noteForm"
+ />
<issue-note-signed-out-widget v-if="!canReply" />
</div>
</div>
diff --git a/app/assets/javascripts/notes/components/issue_note.vue b/app/assets/javascripts/notes/components/issue_note.vue
index 6514ec3b7ab..d490578ce6f 100644
--- a/app/assets/javascripts/notes/components/issue_note.vue
+++ b/app/assets/javascripts/notes/components/issue_note.vue
@@ -1,116 +1,118 @@
<script>
-/* global Flash */
+ /* global Flash */
-import { mapGetters } from 'vuex';
-import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
-import issueNoteHeader from './issue_note_header.vue';
-import issueNoteActions from './issue_note_actions.vue';
-import issueNoteBody from './issue_note_body.vue';
-import eventHub from '../event_hub';
+ import { mapGetters, mapActions } from 'vuex';
+ import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
+ import issueNoteHeader from './issue_note_header.vue';
+ import issueNoteActions from './issue_note_actions.vue';
+ import issueNoteBody from './issue_note_body.vue';
+ import eventHub from '../event_hub';
-export default {
- props: {
- note: {
- type: Object,
- required: true,
+ export default {
+ props: {
+ note: {
+ type: Object,
+ required: true,
+ },
},
- },
- data() {
- return {
- isEditing: false,
- isDeleting: false,
- };
- },
- components: {
- userAvatarLink,
- issueNoteHeader,
- issueNoteActions,
- issueNoteBody,
- },
- computed: {
- ...mapGetters([
- 'targetNoteHash',
- ]),
- author() {
- return this.note.author;
- },
- classNameBindings() {
+ data() {
return {
- 'is-editing': this.isEditing,
- 'disabled-content': this.isDeleting,
- 'js-my-note': this.author.id === window.gon.current_user_id,
- target: this.targetNoteHash === this.noteAnchorId,
+ isEditing: false,
+ isDeleting: false,
};
},
- canReportAsAbuse() {
- return this.note.report_abuse_path && this.author.id !== window.gon.current_user_id;
- },
- noteAnchorId() {
- return `note_${this.note.id}`;
+ components: {
+ userAvatarLink,
+ issueNoteHeader,
+ issueNoteActions,
+ issueNoteBody,
},
- },
- methods: {
- editHandler() {
- this.isEditing = true;
+ computed: {
+ ...mapGetters([
+ 'targetNoteHash',
+ ]),
+ author() {
+ return this.note.author;
+ },
+ classNameBindings() {
+ return {
+ 'is-editing': this.isEditing,
+ 'disabled-content': this.isDeleting,
+ 'js-my-note': this.author.id === window.gon.current_user_id,
+ target: this.targetNoteHash === this.noteAnchorId,
+ };
+ },
+ canReportAsAbuse() {
+ return this.note.report_abuse_path && this.author.id !== window.gon.current_user_id;
+ },
+ noteAnchorId() {
+ return `note_${this.note.id}`;
+ },
},
- deleteHandler() {
- const msg = 'Are you sure you want to delete this list?';
- const isConfirmed = confirm(msg); // eslint-disable-line
+ methods: {
+ ...mapActions([
+ 'deleteNote',
+ 'updateNote',
+ 'scrollToNoteIfNeeded',
+ ]),
+ editHandler() {
+ this.isEditing = true;
+ },
+ deleteHandler() {
+ const msg = 'Are you sure you want to delete this list?';
+ const isConfirmed = confirm(msg); // eslint-disable-line
+
+ if (isConfirmed) {
+ this.isDeleting = true;
+ this.deleteNote(this.note)
+ .then(() => {
+ this.isDeleting = false;
+ })
+ .catch(() => {
+ Flash('Something went wrong while deleting your note. Please try again.');
+ this.isDeleting = false;
+ });
+ }
+ },
+ formUpdateHandler(note) {
+ const data = {
+ endpoint: this.note.path,
+ note: {
+ full_data: true,
+ target_type: 'issue',
+ target_id: this.note.noteable_id,
+ note,
+ },
+ };
- if (isConfirmed) {
- this.isDeleting = true;
- this.$store
- .dispatch('deleteNote', this.note)
+ this.updateNote(data)
.then(() => {
- this.isDeleting = false;
+ this.isEditing = false;
+ $(this.$refs.noteBody.$el).renderGFM();
})
- .catch(() => {
- new Flash('Something went wrong while deleting your note. Please try again.'); // eslint-disable-line
- this.isDeleting = false;
- });
- }
- },
- formUpdateHandler(note) {
- const data = {
- endpoint: this.note.path,
- note: {
- full_data: true,
- target_type: 'issue',
- target_id: this.note.noteable_id,
- note,
- },
- };
+ .catch(() => Flash('Something went wrong while editing your comment. Please try again.'));
+ },
+ formCancelHandler(shouldConfirm) {
+ if (shouldConfirm && this.$refs.noteBody.$refs.noteForm.isDirty) {
+ const msg = 'Are you sure you want to cancel editing this comment?';
+ const isConfirmed = confirm(msg); // eslint-disable-line
+ if (!isConfirmed) {
+ return;
+ }
+ }
- this.$store.dispatch('updateNote', data)
- .then(() => {
- this.isEditing = false;
- $(this.$refs.noteBody.$el).renderGFM();
- })
- .catch(() => {
- Flash('Something went wrong while editing your comment. Please try again.');
- });
+ this.isEditing = false;
+ },
},
- formCancelHandler(shouldConfirm) {
- if (shouldConfirm && this.$refs.noteBody.$refs.noteForm.isDirty) {
- const msg = 'Are you sure you want to cancel editing this comment?';
- const isConfirmed = confirm(msg); // eslint-disable-line
- if (!isConfirmed) {
- return;
+ created() {
+ eventHub.$on('enterEditMode', ({ noteId }) => {
+ if (noteId === this.note.id) {
+ this.isEditing = true;
+ this.scrollToNoteIfNeeded($(this.$el));
}
- }
-
- this.isEditing = false;
+ });
},
- },
- created() {
- eventHub.$on('enterEditMode', ({ noteId }) => {
- if (noteId === this.note.id) {
- this.isEditing = true;
- this.$store.dispatch('scrollToNoteIfNeeded', $(this.$el));
- }
- });
- },
-};
+ };
</script>
<template>
@@ -124,7 +126,8 @@ export default {
:link-href="author.path"
:img-src="author.avatar_url"
:img-alt="author.name"
- :img-size="40" />
+ :img-size="40"
+ />
</div>
<div class="timeline-content">
<div class="note-header">
@@ -132,7 +135,8 @@ export default {
:author="author"
:created-at="note.created_at"
:note-id="note.id"
- actionText="commented" />
+ actionText="commented"
+ />
<issue-note-actions
:author-id="author.id"
:note-id="note.id"
@@ -142,7 +146,8 @@ export default {
:can-report-as-abuse="canReportAsAbuse"
:report-abuse-path="note.report_abuse_path"
:edit-handler="editHandler"
- :delete-handler="deleteHandler" />
+ :delete-handler="deleteHandler"
+ />
</div>
<issue-note-body
:note="note"
@@ -150,7 +155,8 @@ export default {
:is-editing="isEditing"
:form-update-handler="formUpdateHandler"
:form-cancel-handler="formCancelHandler"
- ref="noteBody" />
+ ref="noteBody"
+ />
</div>
</div>
</li>
diff --git a/app/assets/javascripts/notes/components/issue_note_actions.vue b/app/assets/javascripts/notes/components/issue_note_actions.vue
index df5a060d894..6c63cae17cc 100644
--- a/app/assets/javascripts/notes/components/issue_note_actions.vue
+++ b/app/assets/javascripts/notes/components/issue_note_actions.vue
@@ -1,68 +1,71 @@
<script>
-import emojiSmiling from 'icons/_emoji_slightly_smiling_face.svg';
-import emojiSmile from 'icons/_emoji_smile.svg';
-import emojiSmiley from 'icons/_emoji_smiley.svg';
-import loadingIcon from '../../vue_shared/components/loadingIcon.vue';
+ import emojiSmiling from 'icons/_emoji_slightly_smiling_face.svg';
+ import emojiSmile from 'icons/_emoji_smile.svg';
+ import emojiSmiley from 'icons/_emoji_smiley.svg';
+ import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-export default {
- props: {
- authorId: {
- type: Number,
- required: true,
+ export default {
+ props: {
+ authorId: {
+ type: Number,
+ required: true,
+ },
+ noteId: {
+ type: Number,
+ required: true,
+ },
+ accessLevel: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ reportAbusePath: {
+ type: String,
+ required: true,
+ },
+ canEdit: {
+ type: Boolean,
+ required: true,
+ },
+ canDelete: {
+ type: Boolean,
+ required: true,
+ },
+ canReportAsAbuse: {
+ type: Boolean,
+ required: true,
+ },
+ editHandler: {
+ type: Function,
+ required: true,
+ },
+ deleteHandler: {
+ type: Function,
+ required: true,
+ },
},
- noteId: {
- type: Number,
- required: true,
+ data() {
+ return {
+ emojiSmiling,
+ emojiSmile,
+ emojiSmiley,
+ };
},
- accessLevel: {
- type: String,
- required: false,
- default: '',
+ components: {
+ loadingIcon,
},
- reportAbusePath: {
- type: String,
- required: true,
+ computed: {
+ shouldShowActionsDropdown() {
+ return window.gon.current_user_id && (this.canEdit || this.canReportAsAbuse);
+ },
+ canAddAwardEmoji() {
+ return window.gon.current_user_id;
+ },
+ isAuthoredByMe() {
+ return this.authorId === window.gon.current_user_id;
+ },
},
- canEdit: {
- type: Boolean,
- required: true,
- },
- canDelete: {
- type: Boolean,
- required: true,
- },
- canReportAsAbuse: {
- type: Boolean,
- required: true,
- },
- editHandler: {
- type: Function,
- required: true,
- },
- deleteHandler: {
- type: Function,
- required: true,
- },
- },
- data() {
- return {
- emojiSmiling,
- emojiSmile,
- emojiSmiley,
- };
- },
- computed: {
- shouldShowActionsDropdown() {
- return window.gon.current_user_id && (this.canEdit || this.canReportAsAbuse);
- },
- canAddAwardEmoji() {
- return window.gon.current_user_id;
- },
- isAuthoredByMe() {
- return this.authorId === window.gon.current_user_id;
- },
- },
-};
+ };
</script>
<template>
@@ -82,13 +85,16 @@ export default {
<loading-icon />
<span
v-html="emojiSmiling"
- class="link-highlight award-control-icon-neutral"></span>
+ class="link-highlight award-control-icon-neutral">
+ </span>
<span
v-html="emojiSmiley"
- class="link-highlight award-control-icon-positive"></span>
+ class="link-highlight award-control-icon-positive">
+ </span>
<span
v-html="emojiSmile"
- class="link-highlight award-control-icon-super-positive"></span>
+ class="link-highlight award-control-icon-super-positive">
+ </span>
</a>
<div
v-if="shouldShowActionsDropdown"
@@ -101,7 +107,8 @@ export default {
data-container="body">
<i
aria-hidden="true"
- class="fa fa-ellipsis-v icon"></i>
+ class="fa fa-ellipsis-v icon">
+ </i>
</button>
<ul class="dropdown-menu more-actions-dropdown dropdown-open-left">
<template v-if="canEdit">
diff --git a/app/assets/javascripts/notes/components/issue_note_awards_list.vue b/app/assets/javascripts/notes/components/issue_note_awards_list.vue
index c41c608979a..02c0aa0b9c4 100644
--- a/app/assets/javascripts/notes/components/issue_note_awards_list.vue
+++ b/app/assets/javascripts/notes/components/issue_note_awards_list.vue
@@ -1,164 +1,166 @@
<script>
-/* global Flash */
-
-import emojiSmiling from 'icons/_emoji_slightly_smiling_face.svg';
-import emojiSmile from 'icons/_emoji_smile.svg';
-import emojiSmiley from 'icons/_emoji_smiley.svg';
-import * as Emoji from '../../emoji';
-
-export default {
- props: {
- awards: {
- type: Array,
- required: true,
+ /* global Flash */
+
+ import { mapActions } from 'vuex';
+ import emojiSmiling from 'icons/_emoji_slightly_smiling_face.svg';
+ import emojiSmile from 'icons/_emoji_smile.svg';
+ import emojiSmiley from 'icons/_emoji_smiley.svg';
+ import * as Emoji from '../../emoji';
+
+ export default {
+ props: {
+ awards: {
+ type: Array,
+ required: true,
+ },
+ toggleAwardPath: {
+ type: String,
+ required: true,
+ },
+ noteAuthorId: {
+ type: Number,
+ required: true,
+ },
+ noteId: {
+ type: Number,
+ required: true,
+ },
},
- toggleAwardPath: {
- type: String,
- required: true,
- },
- noteAuthorId: {
- type: Number,
- required: true,
- },
- noteId: {
- type: Number,
- required: true,
- },
- },
- data() {
- const userId = window.gon.current_user_id;
-
- return {
- emojiSmiling,
- emojiSmile,
- emojiSmiley,
- canAward: !!userId,
- myUserId: userId,
- };
- },
- computed: {
- // `this.awards` is an array with emojis but they are not grouped by emoji name. See below.
- // [ { name: foo, user: user1 }, { name: bar, user: user1 }, { name: foo, user: user2 } ]
- // This method will group emojis by their name as an Object. See below.
- // {
- // foo: [ { name: foo, user: user1 }, { name: foo, user: user2 } ],
- // bar: [ { name: bar, user: user1 } ]
- // }
- // We need to do this otherwise we will render the same emoji over and over again.
- groupedAwards() {
- const awards = {};
- const orderedAwards = {};
-
- this.awards.forEach((award) => {
- awards[award.name] = awards[award.name] || [];
- awards[award.name].push(award);
- });
-
- // Always show thumbsup and thumbsdown first
- const { thumbsup, thumbsdown } = awards;
- if (thumbsup) {
- orderedAwards.thumbsup = thumbsup;
- delete awards.thumbsup;
- }
- if (thumbsdown) {
- orderedAwards.thumbsdown = thumbsdown;
- delete awards.thumbsdown;
- }
-
- // Because for-in forbidden
- const keys = Object.keys(awards);
- keys.forEach((key) => {
- orderedAwards[key] = awards[key];
- });
-
- return orderedAwards;
- },
- isAuthoredByMe() {
- return this.noteAuthorId === window.gon.current_user_id;
- },
- },
- methods: {
- getAwardHTML(name) {
- return Emoji.glEmojiTag(name);
- },
- getAwardClassBindings(awardList, awardName) {
+ data() {
+ const userId = window.gon.current_user_id;
+
return {
- active: this.amIAwarded(awardList),
- disabled: !this.canInteractWithEmoji(awardList, awardName),
+ emojiSmiling,
+ emojiSmile,
+ emojiSmiley,
+ canAward: !!userId,
+ myUserId: userId,
};
},
- canInteractWithEmoji(awardList, awardName) {
- let isAllowed = true;
- const restrictedEmojis = ['thumbsup', 'thumbsdown'];
- const { myUserId, noteAuthorId } = this;
-
- // Users can not add :+1: and :-1: to their notes
- if (myUserId === noteAuthorId && restrictedEmojis.indexOf(awardName) > -1) {
- isAllowed = false;
- }
+ computed: {
+ // `this.awards` is an array with emojis but they are not grouped by emoji name. See below.
+ // [ { name: foo, user: user1 }, { name: bar, user: user1 }, { name: foo, user: user2 } ]
+ // This method will group emojis by their name as an Object. See below.
+ // {
+ // foo: [ { name: foo, user: user1 }, { name: foo, user: user2 } ],
+ // bar: [ { name: bar, user: user1 } ]
+ // }
+ // We need to do this otherwise we will render the same emoji over and over again.
+ groupedAwards() {
+ const awards = {};
+ const orderedAwards = {};
+
+ this.awards.forEach((award) => {
+ awards[award.name] = awards[award.name] || [];
+ awards[award.name].push(award);
+ });
- return this.canAward && isAllowed;
- },
- amIAwarded(awardList) {
- const isAwarded = awardList.filter(award => award.user.id === this.myUserId);
+ // Always show thumbsup and thumbsdown first
+ const { thumbsup, thumbsdown } = awards;
+ if (thumbsup) {
+ orderedAwards.thumbsup = thumbsup;
+ delete awards.thumbsup;
+ }
+ if (thumbsdown) {
+ orderedAwards.thumbsdown = thumbsdown;
+ delete awards.thumbsdown;
+ }
+
+ // Because for-in forbidden
+ const keys = Object.keys(awards);
+ keys.forEach((key) => {
+ orderedAwards[key] = awards[key];
+ });
- return isAwarded.length;
- },
- awardTitle(awardsList) {
- const amIAwarded = this.amIAwarded(awardsList);
- const TOOLTIP_NAME_COUNT = amIAwarded ? 9 : 10;
- let awardList = awardsList;
-
- // Filter myself from list if I am awarded.
- if (amIAwarded) {
- awardList = awardList.filter(award => award.user.id !== this.myUserId);
- }
-
- // Get only 9-10 usernames to show in tooltip text.
- const namesToShow = awardList.slice(0, TOOLTIP_NAME_COUNT).map(award => award.user.name);
-
- // Get the remaining list to use in `and x more` text.
- const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length);
-
- // Add myself to the begining of the list so title will start with You.
- if (amIAwarded) {
- namesToShow.unshift('You');
- }
-
- let title = '';
-
- // We have 10+ awarded user, join them with comma and add `and x more`.
- if (remainingAwardList.length) {
- title = `${namesToShow.join(', ')}, and ${remainingAwardList.length} more.`;
- } else if (namesToShow.length > 1) {
- // Join all names with comma but not the last one, it will be added with and text.
- title = namesToShow.slice(0, namesToShow.length - 1).join(', ');
- // If we have more than 2 users we need an extra comma before and text.
- title += namesToShow.length > 2 ? ',' : '';
- title += ` and ${namesToShow.slice(-1)}`; // Append and text
- } else { // We have only 2 users so join them with and.
- title = namesToShow.join(' and ');
- }
-
- return title;
+ return orderedAwards;
+ },
+ isAuthoredByMe() {
+ return this.noteAuthorId === window.gon.current_user_id;
+ },
},
- handleAward(awardName) {
- const data = {
- endpoint: this.toggleAwardPath,
- noteId: this.noteId,
- awardName,
- };
-
- this.$store.dispatch('toggleAward', data)
- .then(() => {
- $(this.$el).find('.award-control').tooltip('fixTitle');
- })
- .catch(() => {
- Flash('Something went wrong on our end.');
- });
+ methods: {
+ ...mapActions([
+ 'toggleAward',
+ ]),
+ getAwardHTML(name) {
+ return Emoji.glEmojiTag(name);
+ },
+ getAwardClassBindings(awardList, awardName) {
+ return {
+ active: this.amIAwarded(awardList),
+ disabled: !this.canInteractWithEmoji(awardList, awardName),
+ };
+ },
+ canInteractWithEmoji(awardList, awardName) {
+ let isAllowed = true;
+ const restrictedEmojis = ['thumbsup', 'thumbsdown'];
+ const { myUserId, noteAuthorId } = this;
+
+ // Users can not add :+1: and :-1: to their own notes
+ if (myUserId === noteAuthorId && restrictedEmojis.indexOf(awardName) > -1) {
+ isAllowed = false;
+ }
+
+ return this.canAward && isAllowed;
+ },
+ amIAwarded(awardList) {
+ const isAwarded = awardList.filter(award => award.user.id === this.myUserId);
+
+ return isAwarded.length;
+ },
+ awardTitle(awardsList) {
+ const amIAwarded = this.amIAwarded(awardsList);
+ const TOOLTIP_NAME_COUNT = amIAwarded ? 9 : 10;
+ let awardList = awardsList;
+
+ // Filter myself from list if I am awarded.
+ if (amIAwarded) {
+ awardList = awardList.filter(award => award.user.id !== this.myUserId);
+ }
+
+ // Get only 9-10 usernames to show in tooltip text.
+ const namesToShow = awardList.slice(0, TOOLTIP_NAME_COUNT).map(award => award.user.name);
+
+ // Get the remaining list to use in `and x more` text.
+ const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length);
+
+ // Add myself to the begining of the list so title will start with You.
+ if (amIAwarded) {
+ namesToShow.unshift('You');
+ }
+
+ let title = '';
+
+ // We have 10+ awarded user, join them with comma and add `and x more`.
+ if (remainingAwardList.length) {
+ title = `${namesToShow.join(', ')}, and ${remainingAwardList.length} more.`;
+ } else if (namesToShow.length > 1) {
+ // Join all names with comma but not the last one, it will be added with and text.
+ title = namesToShow.slice(0, namesToShow.length - 1).join(', ');
+ // If we have more than 2 users we need an extra comma before and text.
+ title += namesToShow.length > 2 ? ',' : '';
+ title += ` and ${namesToShow.slice(-1)}`; // Append and text
+ } else { // We have only 2 users so join them with and.
+ title = namesToShow.join(' and ');
+ }
+
+ return title;
+ },
+ handleAward(awardName) {
+ const data = {
+ endpoint: this.toggleAwardPath,
+ noteId: this.noteId,
+ awardName,
+ };
+
+ this.toggleAward(data)
+ .then(() => {
+ $(this.$el).find('.award-control').tooltip('fixTitle');
+ })
+ .catch(() => Flash('Something went wrong on our end.'));
+ },
},
- },
-};
+ };
</script>
<template>
@@ -189,13 +191,16 @@ export default {
type="button">
<span
v-html="emojiSmiling"
- class="award-control-icon award-control-icon-neutral"></span>
+ class="award-control-icon award-control-icon-neutral">
+ </span>
<span
v-html="emojiSmiley"
- class="award-control-icon award-control-icon-positive"></span>
+ class="award-control-icon award-control-icon-positive">
+ </span>
<span
v-html="emojiSmile"
- class="award-control-icon award-control-icon-super-positive"></span>
+ class="award-control-icon award-control-icon-super-positive">
+ </span>
<i
aria-hidden="true"
class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>
diff --git a/app/assets/javascripts/notes/components/issue_note_body.vue b/app/assets/javascripts/notes/components/issue_note_body.vue
index 30c8db6f041..dee8bb0c7f9 100644
--- a/app/assets/javascripts/notes/components/issue_note_body.vue
+++ b/app/assets/javascripts/notes/components/issue_note_body.vue
@@ -1,70 +1,70 @@
<script>
-import issueNoteEditedText from './issue_note_edited_text.vue';
-import issueNoteAwardsList from './issue_note_awards_list.vue';
-import issueNoteForm from './issue_note_form.vue';
-import TaskList from '../../task_list';
+ import issueNoteEditedText from './issue_note_edited_text.vue';
+ import issueNoteAwardsList from './issue_note_awards_list.vue';
+ import issueNoteForm from './issue_note_form.vue';
+ import TaskList from '../../task_list';
-export default {
- props: {
- note: {
- type: Object,
- required: true,
+ export default {
+ props: {
+ note: {
+ type: Object,
+ required: true,
+ },
+ canEdit: {
+ type: Boolean,
+ required: true,
+ },
+ isEditing: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ formUpdateHandler: {
+ type: Function,
+ required: true,
+ },
+ formCancelHandler: {
+ type: Function,
+ required: true,
+ },
},
- canEdit: {
- type: Boolean,
- required: true,
+ components: {
+ issueNoteEditedText,
+ issueNoteAwardsList,
+ issueNoteForm,
},
- isEditing: {
- type: Boolean,
- required: false,
- default: false,
+ computed: {
+ noteBody() {
+ return this.note.note;
+ },
},
- formUpdateHandler: {
- type: Function,
- required: true,
- },
- formCancelHandler: {
- type: Function,
- required: true,
- },
- },
- components: {
- issueNoteEditedText,
- issueNoteAwardsList,
- issueNoteForm,
- },
- computed: {
- noteBody() {
- return this.note.note;
- },
- },
- methods: {
- renderGFM() {
- $(this.$refs['note-body']).renderGFM();
- },
- initTaskList() {
- if (this.canEdit) {
- this.taskList = new TaskList({
- dataType: 'note',
- fieldName: 'note',
- selector: '.notes',
+ methods: {
+ renderGFM() {
+ $(this.$refs['note-body']).renderGFM();
+ },
+ initTaskList() {
+ if (this.canEdit) {
+ this.taskList = new TaskList({
+ dataType: 'note',
+ fieldName: 'note',
+ selector: '.notes',
+ });
+ }
+ },
+ handleFormUpdate() {
+ this.formUpdateHandler({
+ note: this.$refs.noteForm.note,
});
- }
+ },
+ },
+ mounted() {
+ this.renderGFM();
+ this.initTaskList();
},
- handleFormUpdate() {
- this.formUpdateHandler({
- note: this.$refs.noteForm.note,
- });
+ updated() {
+ this.initTaskList();
},
- },
- mounted() {
- this.renderGFM();
- this.initTaskList();
- },
- updated() {
- this.initTaskList();
- },
-};
+ };
</script>
<template>
diff --git a/app/assets/javascripts/notes/components/issue_note_edited_text.vue b/app/assets/javascripts/notes/components/issue_note_edited_text.vue
index aed82fd4a82..5e4f1c82822 100644
--- a/app/assets/javascripts/notes/components/issue_note_edited_text.vue
+++ b/app/assets/javascripts/notes/components/issue_note_edited_text.vue
@@ -1,30 +1,30 @@
<script>
-import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
+ import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
-export default {
- props: {
- actionText: {
- type: String,
- required: true,
+ export default {
+ props: {
+ actionText: {
+ type: String,
+ required: true,
+ },
+ editedAt: {
+ type: String,
+ required: true,
+ },
+ editedBy: {
+ type: Object,
+ required: true,
+ },
+ className: {
+ type: String,
+ required: false,
+ default: 'edited-text',
+ },
},
- editedAt: {
- type: String,
- required: true,
+ components: {
+ timeAgoTooltip,
},
- editedBy: {
- type: Object,
- required: true,
- },
- className: {
- type: String,
- required: false,
- default: 'edited-text',
- },
- },
- components: {
- timeAgoTooltip,
- },
-};
+ };
</script>
<template>
@@ -38,6 +38,7 @@ export default {
</a>
<time-ago-tooltip
:time="editedAt"
- tooltip-placement="bottom" />
+ tooltip-placement="bottom"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/notes/components/issue_note_form.vue b/app/assets/javascripts/notes/components/issue_note_form.vue
index 46ea030ce87..e5d8ef475f9 100644
--- a/app/assets/javascripts/notes/components/issue_note_form.vue
+++ b/app/assets/javascripts/notes/components/issue_note_form.vue
@@ -1,88 +1,88 @@
<script>
-import markdownField from '../../vue_shared/components/markdown/field.vue';
-import eventHub from '../event_hub';
+ import markdownField from '../../vue_shared/components/markdown/field.vue';
+ import eventHub from '../event_hub';
-export default {
- props: {
- noteBody: {
- type: String,
- required: false,
- default: '',
+ export default {
+ props: {
+ noteBody: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ noteId: {
+ type: Number,
+ required: false,
+ },
+ updateHandler: {
+ type: Function,
+ required: true,
+ },
+ cancelHandler: {
+ type: Function,
+ required: true,
+ },
+ saveButtonTitle: {
+ type: String,
+ required: false,
+ default: 'Save comment',
+ },
},
- noteId: {
- type: Number,
- required: false,
+ data() {
+ return {
+ initialNote: this.noteBody,
+ note: this.noteBody,
+ markdownPreviewUrl: gl.issueData.preview_note_path,
+ markdownDocsUrl: '',
+ conflictWhileEditing: false,
+ };
},
- updateHandler: {
- type: Function,
- required: true,
+ components: {
+ markdownField,
},
- cancelHandler: {
- type: Function,
- required: true,
+ computed: {
+ isDirty() {
+ return this.initialNote !== this.note;
+ },
+ noteHash() {
+ return `#note_${this.noteId}`;
+ },
},
- saveButtonTitle: {
- type: String,
- required: false,
- default: 'Save comment',
- },
- },
- data() {
- return {
- initialNote: this.noteBody,
- note: this.noteBody,
- markdownPreviewUrl: gl.issueData.preview_note_path,
- markdownDocsUrl: '',
- conflictWhileEditing: false,
- };
- },
- components: {
- markdownField,
- },
- computed: {
- isDirty() {
- return this.initialNote !== this.note;
- },
- noteHash() {
- return `#note_${this.noteId}`;
- },
- },
- methods: {
- handleUpdate() {
- this.updateHandler({
- note: this.note,
- });
- },
- editMyLastNote() {
- if (this.note === '') {
- const discussion = $(this.$el).closest('.discussion-notes');
- const myLastNoteId = discussion.find('.js-my-note').last().attr('id');
+ methods: {
+ handleUpdate() {
+ this.updateHandler({
+ note: this.note,
+ });
+ },
+ editMyLastNote() {
+ if (this.note === '') {
+ const discussion = $(this.$el).closest('.discussion-notes');
+ const myLastNoteId = discussion.find('.js-my-note').last().attr('id');
- if (myLastNoteId) {
- eventHub.$emit('enterEditMode', {
- noteId: parseInt(myLastNoteId.replace('note_', ''), 10),
- });
+ if (myLastNoteId) {
+ eventHub.$emit('enterEditMode', {
+ noteId: parseInt(myLastNoteId.replace('note_', ''), 10),
+ });
+ }
}
- }
+ },
},
- },
- mounted() {
- const issuableDataEl = document.getElementById('js-issuable-app-initial-data');
- const issueData = JSON.parse(issuableDataEl.innerHTML.replace(/&quot;/g, '"'));
+ mounted() {
+ const issuableDataEl = document.getElementById('js-issuable-app-initial-data');
+ const issueData = JSON.parse(issuableDataEl.innerHTML.replace(/&quot;/g, '"'));
- this.markdownDocsUrl = issueData.markdownDocs;
- this.$refs.textarea.focus();
- },
- watch: {
- noteBody() {
- if (this.note === this.initialNote) {
- this.note = this.noteBody;
- } else {
- this.conflictWhileEditing = true;
- }
+ this.markdownDocsUrl = issueData.markdownDocs;
+ this.$refs.textarea.focus();
+ },
+ watch: {
+ noteBody() {
+ if (this.note === this.initialNote) {
+ this.note = this.noteBody;
+ } else {
+ this.conflictWhileEditing = true;
+ }
+ },
},
- },
-};
+ };
</script>
<template>
diff --git a/app/assets/javascripts/notes/components/issue_note_header.vue b/app/assets/javascripts/notes/components/issue_note_header.vue
index d5249b1a72f..20f23bf828a 100644
--- a/app/assets/javascripts/notes/components/issue_note_header.vue
+++ b/app/assets/javascripts/notes/components/issue_note_header.vue
@@ -1,66 +1,71 @@
<script>
-import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
+ import { mapMutations } from 'vuex';
+ import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
+ import * as types from '../stores/mutation_types';
-export default {
- props: {
- author: {
- type: Object,
- required: true,
+ export default {
+ props: {
+ author: {
+ type: Object,
+ required: true,
+ },
+ createdAt: {
+ type: String,
+ required: true,
+ },
+ actionText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ actionTextHtml: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ noteId: {
+ type: Number,
+ required: true,
+ },
+ includeToggle: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ toggleHandler: {
+ type: Function,
+ required: false,
+ },
},
- createdAt: {
- type: String,
- required: true,
+ data() {
+ return {
+ isExpanded: true,
+ };
},
- actionText: {
- type: String,
- required: false,
- default: '',
+ components: {
+ timeAgoTooltip,
},
- actionTextHtml: {
- type: String,
- required: false,
- default: '',
+ computed: {
+ toggleChevronClass() {
+ return this.isExpanded ? 'fa-chevron-up' : 'fa-chevron-down';
+ },
+ noteTimestampLink() {
+ return `#note_${this.noteId}`;
+ },
},
- noteId: {
- type: Number,
- required: true,
+ methods: {
+ ...mapMutations({
+ setTargetNoteHash: types.SET_TARGET_NOTE_HASH,
+ }),
+ handleToggle() {
+ this.isExpanded = !this.isExpanded;
+ this.toggleHandler();
+ },
+ updateTargetNoteHash() {
+ this.setTargetNoteHash(this.noteTimestampLink);
+ },
},
- includeToggle: {
- type: Boolean,
- required: false,
- default: false,
- },
- toggleHandler: {
- type: Function,
- required: false,
- },
- },
- data() {
- return {
- isExpanded: true,
- };
- },
- components: {
- timeAgoTooltip,
- },
- computed: {
- toggleChevronClass() {
- return this.isExpanded ? 'fa-chevron-up' : 'fa-chevron-down';
- },
- noteTimestampLink() {
- return `#note_${this.noteId}`;
- },
- },
- methods: {
- handleToggle() {
- this.isExpanded = !this.isExpanded;
- this.toggleHandler();
- },
- updateTargetNoteHash() {
- this.$store.commit('setTargetNoteHash', this.noteTimestampLink);
- },
- },
-};
+ };
</script>
<template>
@@ -81,13 +86,15 @@ export default {
<span
v-if="actionTextHtml"
v-html="actionTextHtml"
- class="system-note-message"></span>
+ class="system-note-message">
+ </span>
<a
:href="noteTimestampLink"
@click="updateTargetNoteHash">
<time-ago-tooltip
:time="createdAt"
- tooltipPlacement="bottom" />
+ tooltipPlacement="bottom"
+ />
</a>
</span>
</span>
@@ -101,7 +108,8 @@ export default {
<i
:class="toggleChevronClass"
class="fa"
- aria-hidden="true"></i>
+ aria-hidden="true">
+ </i>
Toggle discussion
</button>
</div>
diff --git a/app/assets/javascripts/notes/components/issue_note_signed_out_widget.vue b/app/assets/javascripts/notes/components/issue_note_signed_out_widget.vue
index 1b819dfcb8b..59d052b35fb 100644
--- a/app/assets/javascripts/notes/components/issue_note_signed_out_widget.vue
+++ b/app/assets/javascripts/notes/components/issue_note_signed_out_widget.vue
@@ -1,18 +1,18 @@
<script>
-export default {
- data() {
- return {
- signInLink: '#',
- };
- },
- mounted() {
- const wrapper = document.querySelector('.js-notes-wrapper');
+ export default {
+ data() {
+ return {
+ signInLink: '#',
+ };
+ },
+ mounted() {
+ const wrapper = document.querySelector('.js-notes-wrapper');
- if (wrapper) {
- this.signInLink = wrapper.dataset.newSessionPath;
- }
- },
-};
+ if (wrapper) {
+ this.signInLink = wrapper.dataset.newSessionPath;
+ }
+ },
+ };
</script>
<template>
diff --git a/app/assets/javascripts/notes/components/issue_notes.vue b/app/assets/javascripts/notes/components/issue_notes.vue
index 2fe071cc990..a3b3bd54cd4 100644
--- a/app/assets/javascripts/notes/components/issue_notes.vue
+++ b/app/assets/javascripts/notes/components/issue_notes.vue
@@ -1,119 +1,131 @@
<script>
-/* global Flash */
+ /* global Flash */
-import Vue from 'vue';
-import Vuex from 'vuex';
-import VueResource from 'vue-resource';
-import storeOptions from '../stores/issue_notes_store';
-import eventHub from '../event_hub';
-import issueNote from './issue_note.vue';
-import issueDiscussion from './issue_discussion.vue';
-import issueSystemNote from './issue_system_note.vue';
-import issueCommentForm from './issue_comment_form.vue';
-import placeholderNote from './issue_placeholder_note.vue';
-import placeholderSystemNote from './issue_placeholder_system_note.vue';
-import store from './store';
+ import Vue from 'vue';
+ import { mapGetters, mapActions, mapMutations } from 'vuex';
+ import store from '../stores/';
+ import * as constants from '../constants'
+ import * as types from '../stores/mutation_types';
+ import eventHub from '../event_hub';
+ import issueNote from './issue_note.vue';
+ import issueDiscussion from './issue_discussion.vue';
+ import issueSystemNote from './issue_system_note.vue';
+ import issueCommentForm from './issue_comment_form.vue';
+ import placeholderNote from './issue_placeholder_note.vue';
+ import placeholderSystemNote from './issue_placeholder_system_note.vue';
+ import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-export default {
- name: 'IssueNotes',
- store,
- data() {
- return {
- isLoading: true,
- };
- },
- components: {
- issueNote,
- issueDiscussion,
- issueSystemNote,
- issueCommentForm,
- placeholderNote,
- placeholderSystemNote,
- },
- computed: {
- ...Vuex.mapGetters([
- 'notes',
- 'notesById',
- ]),
- },
- methods: {
- componentName(note) {
- if (note.isPlaceholderNote) {
- if (note.placeholderType === 'systemNote') {
- return placeholderSystemNote;
- }
- return placeholderNote;
- } else if (note.individual_note) {
- return note.notes[0].system ? issueSystemNote : issueNote;
- }
-
- return issueDiscussion;
+ export default {
+ name: 'IssueNotes',
+ store,
+ data() {
+ return {
+ isLoading: true,
+ };
},
- componentData(note) {
- return note.individual_note ? note.notes[0] : note;
+ components: {
+ issueNote,
+ issueDiscussion,
+ issueSystemNote,
+ issueCommentForm,
+ loadingIcon,
+ placeholderNote,
+ placeholderSystemNote,
},
- fetchNotes() {
- const { discussionsPath } = this.$el.parentNode.dataset;
+ computed: {
+ ...mapGetters([
+ 'notes',
+ 'notesById',
+ ]),
+ },
+ methods: {
+ ...mapActions({
+ actionFetchNotes: 'fetchNotes',
+ }),
+ ...mapActions([
+ 'poll',
+ 'toggleAward',
+ 'scrollToNoteIfNeeded',
+ ]),
+ ...mapMutations({
+ setLastFetchedAt: types.SET_LAST_FETCHED_AT,
+ setTargetNoteHash: types.SET_TARGET_NOTE_HASH,
+ }),
+ getComponentName(note) {
+ if (note.isPlaceholderNote) {
+ if (note.placeholderType === constants.SYSTEM_NOTE) {
+ return placeholderSystemNote;
+ }
+ return placeholderNote;
+ } else if (note.individual_note) {
+ return note.notes[0].system ? issueSystemNote : issueNote;
+ }
- this.$store.dispatch('fetchNotes', discussionsPath)
- .then(() => {
- this.isLoading = false;
+ return issueDiscussion;
+ },
+ getComponentData(note) {
+ return note.individual_note ? note.notes[0] : note;
+ },
+ fetchNotes() {
+ const { discussionsPath } = this.$el.parentNode.dataset;
- // Scroll to note if we have hash fragment in the page URL
- Vue.nextTick(() => {
- this.checkLocationHash();
- });
- })
- .catch(() => {
- Flash('Something went wrong while fetching issue comments. Please try again.');
- });
- },
- initPolling() {
- const { lastFetchedAt } = $('.js-notes-wrapper')[0].dataset;
- this.$store.commit('setLastFetchedAt', lastFetchedAt);
+ this.actionFetchNotes(discussionsPath)
+ .then(() => {
+ this.isLoading = false;
- // FIXME: @fatihacet Implement real polling mechanism
- setInterval(() => {
- this.$store.dispatch('poll')
- .then((res) => {
- this.$store.commit('setLastFetchedAt', res.lastFetchedAt);
+ // Scroll to note if we have hash fragment in the page URL
+ Vue.nextTick(() => {
+ this.checkLocationHash();
+ });
})
.catch(() => {
- Flash('Something went wrong while fetching latest comments.');
+ Flash('Something went wrong while fetching issue comments. Please try again.');
});
- }, 15000);
- },
- bindEventHubListeners() {
- eventHub.$on('toggleAward', (data) => {
- const { awardName, noteId } = data;
- const endpoint = this.notesById[noteId].toggle_award_path;
+ },
+ initPolling() {
+ const { lastFetchedAt } = $('.js-notes-wrapper')[0].dataset;
+ this.setLastFetchedAt(lastFetchedAt);
- this.$store.dispatch('toggleAward', { endpoint, awardName, noteId })
- .catch(() => {
- Flash('Something went wrong on our end.');
- });
- });
+ // FIXME: @fatihacet Implement real polling mechanism
+ setInterval(() => {
+ this.poll()
+ .then((res) => {
+ this.setLastFetchedAt(res.lastFetchedAt);
+ })
+ .catch(() => {
+ Flash('Something went wrong while fetching latest comments.');
+ });
+ }, 15000);
+ },
+ bindEventHubListeners() {
+ eventHub.$on('toggleAward', (data) => {
+ const { awardName, noteId } = data;
+ const endpoint = this.notesById[noteId].toggle_award_path;
- $(document).on('issuable:change', (e, isClosed) => {
- eventHub.$emit('issueStateChanged', isClosed);
- });
- },
- checkLocationHash() {
- const hash = gl.utils.getLocationHash();
- const $el = $(`#${hash}`);
+ this.toggleAward({ endpoint, awardName, noteId })
+ .catch(() => {new Flash('Something went wrong on our end.')});
+ });
- if (hash && $el) {
- this.$store.commit('setTargetNoteHash', hash);
- this.$store.dispatch('scrollToNoteIfNeeded', $el);
- }
+ $(document).on('issuable:change', (e, isClosed) => {
+ eventHub.$emit('issueStateChanged', isClosed);
+ });
+ },
+ checkLocationHash() {
+ const hash = gl.utils.getLocationHash();
+ const $el = $(`#${hash}`);
+
+ if (hash && $el) {
+ this.setTargetNoteHash(hash);
+ this.scrollToNoteIfNeeded($el);
+ }
+ },
+ },
+ mounted() {
+ this.fetchNotes();
+ this.initPolling();
+ this.bindEventHubListeners();
},
- },
- mounted() {
- this.fetchNotes();
- this.initPolling();
- this.bindEventHubListeners();
- },
-};
+ };
</script>
<template>
@@ -121,9 +133,7 @@ export default {
<div
v-if="isLoading"
class="loading">
- <i
- class="fa fa-spinner fa-spin"
- aria-hidden="true"></i>
+ <loading-icon />
</div>
<ul
v-if="!isLoading"
@@ -131,9 +141,10 @@ export default {
class="notes main-notes-list timeline">
<component
v-for="note in notes"
- :is="componentName(note)"
- :note="componentData(note)"
- :key="note.id" />
+ :is="getComponentName(note)"
+ :note="getComponentData(note)"
+ :key="note.id"
+ />
</ul>
<issue-comment-form v-if="!isLoading" />
</div>
diff --git a/app/assets/javascripts/notes/components/issue_placeholder_note.vue b/app/assets/javascripts/notes/components/issue_placeholder_note.vue
index af249c56a3d..fec60d9667b 100644
--- a/app/assets/javascripts/notes/components/issue_placeholder_note.vue
+++ b/app/assets/javascripts/notes/components/issue_placeholder_note.vue
@@ -1,17 +1,17 @@
<script>
-export default {
- props: {
- note: {
- type: Object,
- required: true,
+ export default {
+ props: {
+ note: {
+ type: Object,
+ required: true,
+ },
},
- },
- data() {
- return {
- currentUser: window.gl.currentUserData,
- };
- },
-};
+ data() {
+ return {
+ currentUser: window.gl.currentUserData,
+ };
+ },
+ };
</script>
<template>
@@ -21,7 +21,8 @@ export default {
<a :href="currentUser.path">
<img
:src="currentUser.avatar_url"
- class="avatar s40" />
+ class="avatar s40"
+ />
</a>
</div>
<div
diff --git a/app/assets/javascripts/notes/components/issue_placeholder_system_note.vue b/app/assets/javascripts/notes/components/issue_placeholder_system_note.vue
index 6738d82e7e7..b0d442c1a87 100644
--- a/app/assets/javascripts/notes/components/issue_placeholder_system_note.vue
+++ b/app/assets/javascripts/notes/components/issue_placeholder_system_note.vue
@@ -1,12 +1,12 @@
<script>
-export default {
- props: {
- note: {
- type: Object,
- required: true,
+ export default {
+ props: {
+ note: {
+ type: Object,
+ required: true,
+ },
},
- },
-};
+ };
</script>
<template>
diff --git a/app/assets/javascripts/notes/components/issue_system_note.vue b/app/assets/javascripts/notes/components/issue_system_note.vue
index 65ddaccbcea..91e1c62bba0 100644
--- a/app/assets/javascripts/notes/components/issue_system_note.vue
+++ b/app/assets/javascripts/notes/components/issue_system_note.vue
@@ -1,35 +1,35 @@
<script>
-import { mapGetters } from 'vuex';
-import iconsMap from './issue_note_icons';
-import issueNoteHeader from './issue_note_header.vue';
+ import { mapGetters } from 'vuex';
+ import iconsMap from './issue_note_icons';
+ import issueNoteHeader from './issue_note_header.vue';
-export default {
- props: {
- note: {
- type: Object,
- required: true,
+ export default {
+ props: {
+ note: {
+ type: Object,
+ required: true,
+ },
},
- },
- data() {
- return {
- svg: iconsMap[this.note.system_note_icon_name],
- };
- },
- components: {
- issueNoteHeader,
- },
- computed: {
- ...mapGetters([
- 'targetNoteHash',
- ]),
- noteAnchorId() {
- return `note_${this.note.id}`;
+ data() {
+ return {
+ svg: iconsMap[this.note.system_note_icon_name],
+ };
},
- isTargetNote() {
- return this.targetNoteHash === this.noteAnchorId;
+ components: {
+ issueNoteHeader,
},
- },
-};
+ computed: {
+ ...mapGetters([
+ 'targetNoteHash',
+ ]),
+ noteAnchorId() {
+ return `note_${this.note.id}`;
+ },
+ isTargetNote() {
+ return this.targetNoteHash === this.noteAnchorId;
+ },
+ },
+ };
</script>
<template>
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index 663434587a2..53fb03bab41 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -5,4 +5,4 @@ export const SYSTEM_NOTE = 'systemNote';
export const COMMENT = 'comment';
export const OPENED = 'opened';
export const REOPENED = 'reopened';
-export const CLOSED = 'closed'; \ No newline at end of file
+export const CLOSED = 'closed';
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 0f81e08a2c8..092049cb377 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -138,8 +138,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
export const poll = ({ commit, state, getters }) => {
const { notesPath } = $('.js-notes-wrapper')[0].dataset;
- return service
- .poll(`${notesPath}?full_data=1`, state.lastFetchedAt)
+ return service.poll(`${notesPath}?full_data=1`, state.lastFetchedAt)
.then(res => res.json())
.then((res) => {
if (res.notes.length) {
@@ -188,8 +187,8 @@ export const toggleAward = ({ commit, getters, dispatch }, data) => {
});
if (amIAwarded) {
- Object.assign(data, { awardName: counterAward });
- Object.assign(data, { skipMutalityCheck: true });
+ data.awardName = counterAward;
+ data.skipMutalityCheck = true;
dispatch(types.TOGGLE_AWARD, data);
}