debian-mirror-gitlab/app/assets/javascripts/notes/components/notes_app.vue

342 lines
10 KiB
Vue
Raw Normal View History

2018-03-17 18:26:18 +05:30
<script>
2018-05-09 12:01:36 +05:30
import { mapGetters, mapActions } from 'vuex';
2021-03-11 19:13:27 +05:30
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
2021-09-30 23:02:18 +05:30
import createFlash from '~/flash';
2021-03-11 19:13:27 +05:30
import { __ } from '~/locale';
import initUserPopovers from '~/user_popovers';
2021-04-29 21:17:54 +05:30
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
2021-03-11 19:13:27 +05:30
import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
2021-04-29 21:17:54 +05:30
import draftNote from '../../batch_comments/components/draft_note.vue';
2021-03-11 19:13:27 +05:30
import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility';
2018-05-09 12:01:36 +05:30
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';
2021-03-11 19:13:27 +05:30
import systemNote from '../../vue_shared/components/notes/system_note.vue';
import * as constants from '../constants';
import eventHub from '../event_hub';
import commentForm from './comment_form.vue';
import discussionFilterNote from './discussion_filter_note.vue';
import noteableDiscussion from './noteable_discussion.vue';
import noteableNote from './noteable_note.vue';
2021-04-17 20:07:23 +05:30
import SidebarSubscription from './sidebar_subscription.vue';
2018-03-17 18:26:18 +05:30
2018-05-09 12:01:36 +05:30
export default {
name: 'NotesApp',
components: {
noteableNote,
noteableDiscussion,
systemNote,
commentForm,
placeholderNote,
placeholderSystemNote,
2019-02-15 15:39:39 +05:30
skeletonLoadingContainer,
2019-07-07 11:18:12 +05:30
discussionFilterNote,
2020-04-22 19:07:51 +05:30
OrderedLayout,
2021-04-17 20:07:23 +05:30
SidebarSubscription,
2021-04-29 21:17:54 +05:30
draftNote,
TimelineEntryItem,
2018-05-09 12:01:36 +05:30
},
2021-03-11 19:13:27 +05:30
mixins: [glFeatureFlagsMixin()],
2018-05-09 12:01:36 +05:30
props: {
noteableData: {
type: Object,
required: true,
2018-03-17 18:26:18 +05:30
},
2018-05-09 12:01:36 +05:30
notesData: {
type: Object,
required: true,
2018-03-17 18:26:18 +05:30
},
2018-05-09 12:01:36 +05:30
userData: {
type: Object,
required: false,
default: () => ({}),
2018-03-17 18:26:18 +05:30
},
2018-11-08 19:23:39 +05:30
shouldShow: {
type: Boolean,
required: false,
default: true,
},
2019-02-15 15:39:39 +05:30
helpPagePath: {
type: String,
required: false,
default: '',
},
2018-05-09 12:01:36 +05:30
},
data() {
return {
2018-12-13 13:39:08 +05:30
currentFilter: null,
2021-09-30 23:02:18 +05:30
renderSkeleton: !this.shouldShow,
2018-05-09 12:01:36 +05:30
};
},
computed: {
2018-12-13 13:39:08 +05:30
...mapGetters([
'isNotesFetched',
'discussions',
2019-07-07 11:18:12 +05:30
'convertedDisscussionIds',
2018-12-13 13:39:08 +05:30
'getNotesDataByProp',
'isLoading',
2021-03-11 19:13:27 +05:30
'isFetching',
2018-12-13 13:39:08 +05:30
'commentsDisabled',
2019-07-07 11:18:12 +05:30
'getNoteableData',
2019-07-31 22:56:46 +05:30
'userCanReply',
2019-12-21 20:55:43 +05:30
'discussionTabCounter',
2020-04-22 19:07:51 +05:30
'sortDirection',
2021-01-03 14:25:43 +05:30
'timelineEnabled',
2018-12-13 13:39:08 +05:30
]),
2020-04-22 19:07:51 +05:30
sortDirDesc() {
return this.sortDirection === constants.DESC;
},
2020-01-01 13:55:28 +05:30
discussionTabCounterText() {
return this.isLoading ? '' : this.discussionTabCounter;
},
2018-05-09 12:01:36 +05:30
noteableType() {
return this.noteableData.noteableType;
2018-03-17 18:26:18 +05:30
},
2018-11-08 19:23:39 +05:30
allDiscussions() {
2021-12-11 22:18:48 +05:30
let skeletonNotes = [];
2021-09-30 23:02:18 +05:30
if (this.renderSkeleton || this.isLoading) {
2019-12-04 20:38:33 +05:30
const prerenderedNotesCount = parseInt(this.notesData.prerenderedNotesCount, 10) || 0;
2018-03-17 18:26:18 +05:30
2021-12-11 22:18:48 +05:30
skeletonNotes = new Array(prerenderedNotesCount).fill({
2018-05-09 12:01:36 +05:30
isSkeletonNote: true,
2018-03-17 18:26:18 +05:30
});
}
2018-11-08 19:23:39 +05:30
2022-01-26 12:08:38 +05:30
if (this.sortDirDesc) {
return skeletonNotes.concat(this.discussions);
}
2021-12-11 22:18:48 +05:30
return this.discussions.concat(skeletonNotes);
2018-11-08 19:23:39 +05:30
},
2019-07-07 11:18:12 +05:30
canReply() {
2021-01-03 14:25:43 +05:30
return this.userCanReply && !this.commentsDisabled && !this.timelineEnabled;
2019-07-07 11:18:12 +05:30
},
2020-04-22 19:07:51 +05:30
slotKeys() {
return this.sortDirDesc ? ['form', 'comments'] : ['comments', 'form'];
},
2018-11-08 19:23:39 +05:30
},
watch: {
2021-03-11 19:13:27 +05:30
async isFetching() {
if (!this.isFetching) {
await this.$nextTick();
await this.startTaskList();
await this.checkLocationHash();
}
},
2018-11-08 19:23:39 +05:30
shouldShow() {
if (!this.isNotesFetched) {
this.fetchNotes();
}
2021-09-30 23:02:18 +05:30
setTimeout(() => {
this.renderSkeleton = !this.shouldShow;
});
2018-03-27 19:54:05 +05:30
},
2020-01-01 13:55:28 +05:30
discussionTabCounterText(val) {
if (this.discussionsCount) {
this.discussionsCount.textContent = val;
2019-07-07 11:18:12 +05:30
}
},
2018-05-09 12:01:36 +05:30
},
created() {
2019-12-21 20:55:43 +05:30
this.discussionsCount = document.querySelector('.js-discussions-count');
2019-07-07 11:18:12 +05:30
2018-05-09 12:01:36 +05:30
this.setNotesData(this.notesData);
this.setNoteableData(this.noteableData);
this.setUserData(this.userData);
2018-11-08 19:23:39 +05:30
this.setTargetNoteHash(getLocationHash());
eventHub.$once('fetchNotesData', this.fetchNotes);
2018-05-09 12:01:36 +05:30
},
mounted() {
2018-11-08 19:23:39 +05:30
if (this.shouldShow) {
this.fetchNotes();
}
2018-05-09 12:01:36 +05:30
2018-11-08 19:23:39 +05:30
const { parentElement } = this.$el;
if (parentElement && parentElement.classList.contains('js-vue-notes-event')) {
2021-03-08 18:12:59 +05:30
parentElement.addEventListener('toggleAward', (event) => {
2018-05-09 12:01:36 +05:30
const { awardName, noteId } = event.detail;
2019-02-15 15:39:39 +05:30
this.toggleAward({ awardName, noteId });
2018-05-09 12:01:36 +05:30
});
}
2019-12-26 22:10:19 +05:30
window.addEventListener('hashchange', this.handleHashChanged);
2020-10-24 23:57:45 +05:30
eventHub.$on('notesApp.updateIssuableConfidentiality', this.setConfidentiality);
2018-05-09 12:01:36 +05:30
},
2018-12-05 23:21:45 +05:30
updated() {
2019-02-15 15:39:39 +05:30
this.$nextTick(() => {
highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member'));
initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
});
2018-12-05 23:21:45 +05:30
},
2019-09-04 21:01:54 +05:30
beforeDestroy() {
this.stopPolling();
2019-12-26 22:10:19 +05:30
window.removeEventListener('hashchange', this.handleHashChanged);
2020-10-24 23:57:45 +05:30
eventHub.$off('notesApp.updateIssuableConfidentiality', this.setConfidentiality);
2019-09-04 21:01:54 +05:30
},
2018-05-09 12:01:36 +05:30
methods: {
2019-02-15 15:39:39 +05:30
...mapActions([
2021-03-11 19:13:27 +05:30
'setFetchingState',
2019-02-15 15:39:39 +05:30
'setLoadingState',
'fetchDiscussions',
'poll',
'toggleAward',
'setNotesData',
'setNoteableData',
'setUserData',
'setLastFetchedAt',
'setTargetNoteHash',
'toggleDiscussion',
'setNotesFetchedState',
'expandDiscussion',
'startTaskList',
2019-07-07 11:18:12 +05:30
'convertToDiscussion',
2019-09-04 21:01:54 +05:30
'stopPolling',
2020-10-24 23:57:45 +05:30
'setConfidentiality',
2019-02-15 15:39:39 +05:30
]),
2020-04-22 19:07:51 +05:30
discussionIsIndividualNoteAndNotConverted(discussion) {
return discussion.individual_note && !this.convertedDisscussionIds.includes(discussion.id);
},
2019-12-26 22:10:19 +05:30
handleHashChanged() {
const noteId = this.checkLocationHash();
if (noteId) {
this.setTargetNoteHash(getLocationHash());
}
},
2018-05-09 12:01:36 +05:30
fetchNotes() {
2018-12-13 13:39:08 +05:30
if (this.isFetching) return null;
2021-03-11 19:13:27 +05:30
this.setFetchingState(true);
if (this.glFeatures.paginatedNotes) {
return this.initPolling();
}
2018-12-13 13:39:08 +05:30
2019-12-21 20:55:43 +05:30
return this.fetchDiscussions(this.getFetchDiscussionsConfig())
.then(this.initPolling)
2018-05-09 12:01:36 +05:30
.then(() => {
2018-12-13 13:39:08 +05:30
this.setLoadingState(false);
2018-11-08 19:23:39 +05:30
this.setNotesFetchedState(true);
2018-11-20 20:47:30 +05:30
eventHub.$emit('fetchedNotesData');
2021-03-11 19:13:27 +05:30
this.setFetchingState(false);
2018-05-09 12:01:36 +05:30
})
.catch(() => {
2018-12-13 13:39:08 +05:30
this.setLoadingState(false);
2018-11-08 19:23:39 +05:30
this.setNotesFetchedState(true);
2021-09-30 23:02:18 +05:30
createFlash({
message: __('Something went wrong while fetching comments. Please try again.'),
});
2018-05-09 12:01:36 +05:30
});
},
initPolling() {
if (this.isPollingInitialized) {
return;
}
2018-03-27 19:54:05 +05:30
2018-05-09 12:01:36 +05:30
this.setLastFetchedAt(this.getNotesDataByProp('lastFetchedAt'));
2018-03-17 18:26:18 +05:30
2018-05-09 12:01:36 +05:30
this.poll();
this.isPollingInitialized = true;
},
checkLocationHash() {
const hash = getLocationHash();
2018-11-08 19:23:39 +05:30
const noteId = hash && hash.replace(/^note_/, '');
2018-03-17 18:26:18 +05:30
2018-11-08 19:23:39 +05:30
if (noteId) {
2021-03-08 18:12:59 +05:30
const discussion = this.discussions.find((d) => d.notes.some(({ id }) => id === noteId));
2019-02-15 15:39:39 +05:30
if (discussion) {
this.expandDiscussion({ discussionId: discussion.id });
}
2018-05-09 12:01:36 +05:30
}
2019-12-26 22:10:19 +05:30
return noteId;
2018-03-17 18:26:18 +05:30
},
2019-07-07 11:18:12 +05:30
startReplying(discussionId) {
return this.convertToDiscussion(discussionId)
2019-12-21 20:55:43 +05:30
.then(this.$nextTick)
2019-07-07 11:18:12 +05:30
.then(() => eventHub.$emit('startReplying', discussionId));
},
2019-12-21 20:55:43 +05:30
getFetchDiscussionsConfig() {
const defaultConfig = { path: this.getNotesDataByProp('discussionsPath') };
2021-12-11 22:18:48 +05:30
const currentFilter =
this.getNotesDataByProp('notesFilter') || constants.DISCUSSION_FILTERS_DEFAULT_VALUE;
if (
doesHashExistInUrl(constants.NOTE_UNDERSCORE) &&
currentFilter !== constants.DISCUSSION_FILTERS_DEFAULT_VALUE
) {
2020-05-24 23:13:21 +05:30
return {
...defaultConfig,
2019-12-21 20:55:43 +05:30
filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE,
persistFilter: false,
2020-05-24 23:13:21 +05:30
};
2019-12-21 20:55:43 +05:30
}
return defaultConfig;
},
2018-05-09 12:01:36 +05:30
},
2019-02-15 15:39:39 +05:30
systemNote: constants.SYSTEM_NOTE,
2018-05-09 12:01:36 +05:30
};
2018-03-17 18:26:18 +05:30
</script>
<template>
2019-02-15 15:39:39 +05:30
<div v-show="shouldShow" id="notes">
2021-04-17 20:07:23 +05:30
<sidebar-subscription :iid="noteableData.iid" :noteable-data="noteableData" />
2020-04-22 19:07:51 +05:30
<ordered-layout :slot-keys="slotKeys">
<template #form>
<comment-form
2021-01-03 14:25:43 +05:30
v-if="!(commentsDisabled || timelineEnabled)"
2020-04-22 19:07:51 +05:30
class="js-comment-form"
:noteable-type="noteableType"
2019-02-15 15:39:39 +05:30
/>
</template>
2020-04-22 19:07:51 +05:30
<template #comments>
<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" />
2021-04-29 21:17:54 +05:30
<timeline-entry-item v-else-if="discussion.isDraft" :key="discussion.id">
<draft-note :draft="discussion" />
</timeline-entry-item>
2020-04-22 19:07:51 +05:30
<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="discussionIsIndividualNoteAndNotConverted(discussion)">
<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"
2021-11-18 22:05:49 +05:30
is-overview-tab
2020-04-22 19:07:51 +05:30
:help-page-path="helpPagePath"
/>
</template>
<discussion-filter-note v-show="commentsDisabled" />
</ul>
</template>
</ordered-layout>
2018-03-17 18:26:18 +05:30
</div>
</template>