gitlab-org--gitlab-foss/app/assets/javascripts/notes/components/notes_app.vue

247 lines
6.9 KiB
Vue
Raw Normal View History

<script>
2018-03-16 16:16:21 -04:00
import { mapGetters, mapActions } from 'vuex';
import { getLocationHash } from '../../lib/utils/url_utility';
import Flash from '../../flash';
import * as constants from '../constants';
import eventHub from '../event_hub';
2018-03-16 16:16:21 -04:00
import noteableNote from './noteable_note.vue';
import noteableDiscussion from './noteable_discussion.vue';
import discussionFilterNote from './discussion_filter_note.vue';
2018-03-16 16:16:21 -04:00
import systemNote from '../../vue_shared/components/notes/system_note.vue';
import commentForm from './comment_form.vue';
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
import skeletonLoadingContainer from '../../vue_shared/components/notes/skeleton_note.vue';
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
import initUserPopovers from '../../user_popovers';
2018-03-16 16:16:21 -04:00
export default {
name: 'NotesApp',
components: {
noteableNote,
noteableDiscussion,
systemNote,
commentForm,
placeholderNote,
placeholderSystemNote,
skeletonLoadingContainer,
discussionFilterNote,
2018-03-16 16:16:21 -04:00
},
props: {
noteableData: {
type: Object,
required: true,
2018-01-04 19:18:35 -05:00
},
2018-03-16 16:16:21 -04:00
notesData: {
type: Object,
required: true,
},
2018-03-16 16:16:21 -04:00
userData: {
type: Object,
required: false,
default: () => ({}),
},
2018-06-21 08:22:40 -04:00
shouldShow: {
type: Boolean,
required: false,
default: true,
},
helpPagePath: {
type: String,
required: false,
default: '',
},
2018-03-16 16:16:21 -04:00
},
data() {
return {
isFetching: false,
currentFilter: null,
2018-03-16 16:16:21 -04:00
};
},
computed: {
...mapGetters([
'isNotesFetched',
'discussions',
'convertedDisscussionIds',
'getNotesDataByProp',
'isLoading',
'commentsDisabled',
'getNoteableData',
'userCanReply',
]),
2018-03-16 16:16:21 -04:00
noteableType() {
return this.noteableData.noteableType;
2018-01-04 19:18:35 -05:00
},
2018-06-21 08:22:40 -04:00
allDiscussions() {
2018-03-16 16:16:21 -04:00
if (this.isLoading) {
const totalNotes = parseInt(this.notesData.totalNotes, 10) || 0;
2018-01-04 19:18:35 -05:00
2018-03-16 16:16:21 -04:00
return new Array(totalNotes).fill({
isSkeletonNote: true,
2018-01-04 19:18:35 -05:00
});
}
2018-06-21 08:22:40 -04:00
return this.discussions;
2018-01-04 19:18:35 -05:00
},
canReply() {
return this.userCanReply && !this.commentsDisabled;
},
2018-03-16 16:16:21 -04:00
},
watch: {
shouldShow() {
if (!this.isNotesFetched) {
this.fetchNotes();
}
},
allDiscussions() {
if (this.discussonsCount) {
this.discussonsCount.textContent = this.allDiscussions.length;
}
},
},
2018-03-16 16:16:21 -04:00
created() {
this.discussonsCount = document.querySelector('.js-discussions-count');
2018-03-16 16:16:21 -04:00
this.setNotesData(this.notesData);
this.setNoteableData(this.noteableData);
this.setUserData(this.userData);
2018-06-21 08:22:40 -04:00
this.setTargetNoteHash(getLocationHash());
eventHub.$once('fetchNotesData', this.fetchNotes);
2018-03-16 16:16:21 -04:00
},
mounted() {
if (this.shouldShow) {
this.fetchNotes();
}
const { parentElement } = this.$el;
2018-06-21 08:22:40 -04:00
if (parentElement && parentElement.classList.contains('js-vue-notes-event')) {
2018-03-16 16:16:21 -04:00
parentElement.addEventListener('toggleAward', event => {
const { awardName, noteId } = event.detail;
this.toggleAward({ awardName, noteId });
2018-03-16 16:16:21 -04:00
});
}
},
updated() {
this.$nextTick(() => {
highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member'));
initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
});
},
2018-03-16 16:16:21 -04:00
methods: {
...mapActions([
'setLoadingState',
'fetchDiscussions',
'poll',
'toggleAward',
'scrollToNoteIfNeeded',
'setNotesData',
'setNoteableData',
'setUserData',
'setLastFetchedAt',
'setTargetNoteHash',
'toggleDiscussion',
'setNotesFetchedState',
'expandDiscussion',
'startTaskList',
'convertToDiscussion',
]),
2018-03-16 16:16:21 -04:00
fetchNotes() {
if (this.isFetching) return null;
this.isFetching = true;
return this.fetchDiscussions({ path: this.getNotesDataByProp('discussionsPath') })
2018-06-21 08:22:40 -04:00
.then(() => {
this.initPolling();
})
2018-03-16 16:16:21 -04:00
.then(() => {
this.setLoadingState(false);
this.setNotesFetchedState(true);
eventHub.$emit('fetchedNotesData');
this.isFetching = false;
2018-03-16 16:16:21 -04:00
})
.then(() => this.$nextTick())
.then(() => this.startTaskList())
2018-03-16 16:16:21 -04:00
.then(() => this.checkLocationHash())
.catch(() => {
this.setLoadingState(false);
this.setNotesFetchedState(true);
2018-06-21 08:22:40 -04:00
Flash('Something went wrong while fetching comments. Please try again.');
2018-03-16 16:16:21 -04:00
});
},
initPolling() {
if (this.isPollingInitialized) {
return;
}
2018-03-16 16:16:21 -04:00
this.setLastFetchedAt(this.getNotesDataByProp('lastFetchedAt'));
2018-03-16 16:16:21 -04:00
this.poll();
this.isPollingInitialized = true;
},
checkLocationHash() {
const hash = getLocationHash();
2018-06-21 08:22:40 -04:00
const noteId = hash && hash.replace(/^note_/, '');
2018-03-16 16:16:21 -04:00
2018-06-21 08:22:40 -04:00
if (noteId) {
const discussion = this.discussions.find(d => d.notes.some(({ id }) => id === noteId));
if (discussion) {
this.expandDiscussion({ discussionId: discussion.id });
}
2018-03-16 16:16:21 -04:00
}
},
startReplying(discussionId) {
return this.convertToDiscussion(discussionId)
.then(() => this.$nextTick())
.then(() => eventHub.$emit('startReplying', discussionId));
},
2018-03-16 16:16:21 -04:00
},
systemNote: constants.SYSTEM_NOTE,
2018-03-16 16:16:21 -04:00
};
</script>
<template>
2018-11-16 15:07:38 -05:00
<div v-show="shouldShow" id="notes">
<ul id="notes-list" class="notes main-notes-list timeline">
<template v-for="discussion in allDiscussions">
<skeleton-loading-container v-if="discussion.isSkeletonNote" :key="discussion.id" />
<template v-else-if="discussion.isPlaceholderNote">
<placeholder-system-note
v-if="discussion.placeholderType === $options.systemNote"
:key="discussion.id"
:note="discussion.notes[0]"
/>
<placeholder-note v-else :key="discussion.id" :note="discussion.notes[0]" />
</template>
<template
v-else-if="discussion.individual_note && !convertedDisscussionIds.includes(discussion.id)"
>
<system-note
v-if="discussion.notes[0].system"
:key="discussion.id"
:note="discussion.notes[0]"
/>
<noteable-note
v-else
:key="discussion.id"
:note="discussion.notes[0]"
:show-reply-button="canReply"
@startReplying="startReplying(discussion.id)"
/>
</template>
<noteable-discussion
v-else
:key="discussion.id"
:discussion="discussion"
:render-diff-file="true"
:help-page-path="helpPagePath"
/>
</template>
<discussion-filter-note v-show="commentsDisabled" />
</ul>
<comment-form v-if="!commentsDisabled" :noteable-type="noteableType" />
</div>
</template>