notes_app.vue 5.96 KB
Newer Older
1
<script>
Fatih Acet's avatar
Fatih Acet committed
2 3 4 5
import { mapGetters, mapActions } from 'vuex';
import { getLocationHash } from '../../lib/utils/url_utility';
import Flash from '../../flash';
import * as constants from '../constants';
6
import eventHub from '../event_hub';
Fatih Acet's avatar
Fatih Acet committed
7 8 9 10 11 12 13
import noteableNote from './noteable_note.vue';
import noteableDiscussion from './noteable_discussion.vue';
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';
14
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
Tim Zallmann's avatar
Tim Zallmann committed
15
import initUserPopovers from '../../user_popovers';
16

Fatih Acet's avatar
Fatih Acet committed
17 18 19 20 21 22 23 24 25
export default {
  name: 'NotesApp',
  components: {
    noteableNote,
    noteableDiscussion,
    systemNote,
    commentForm,
    placeholderNote,
    placeholderSystemNote,
26
    skeletonLoadingContainer,
Fatih Acet's avatar
Fatih Acet committed
27 28 29 30 31
  },
  props: {
    noteableData: {
      type: Object,
      required: true,
Filipa Lacerda's avatar
Filipa Lacerda committed
32
    },
Fatih Acet's avatar
Fatih Acet committed
33 34 35
    notesData: {
      type: Object,
      required: true,
36
    },
Fatih Acet's avatar
Fatih Acet committed
37 38 39 40
    userData: {
      type: Object,
      required: false,
      default: () => ({}),
41
    },
Felipe Artur's avatar
Felipe Artur committed
42 43 44 45 46
    shouldShow: {
      type: Boolean,
      required: false,
      default: true,
    },
47 48 49 50 51
    markdownVersion: {
      type: Number,
      required: false,
      default: 0,
    },
Fatih Acet's avatar
Fatih Acet committed
52 53 54
  },
  data() {
    return {
55
      isFetching: false,
56
      currentFilter: null,
Fatih Acet's avatar
Fatih Acet committed
57 58 59
    };
  },
  computed: {
60 61 62 63 64
    ...mapGetters([
      'isNotesFetched',
      'discussions',
      'getNotesDataByProp',
      'isLoading',
65
      'commentsDisabled',
66
    ]),
Fatih Acet's avatar
Fatih Acet committed
67
    noteableType() {
68
      return this.noteableData.noteableType;
Filipa Lacerda's avatar
Filipa Lacerda committed
69
    },
Felipe Artur's avatar
Felipe Artur committed
70
    allDiscussions() {
Fatih Acet's avatar
Fatih Acet committed
71 72
      if (this.isLoading) {
        const totalNotes = parseInt(this.notesData.totalNotes, 10) || 0;
Filipa Lacerda's avatar
Filipa Lacerda committed
73

Fatih Acet's avatar
Fatih Acet committed
74 75
        return new Array(totalNotes).fill({
          isSkeletonNote: true,
Filipa Lacerda's avatar
Filipa Lacerda committed
76 77
        });
      }
78

Felipe Artur's avatar
Felipe Artur committed
79
      return this.discussions;
Filipa Lacerda's avatar
Filipa Lacerda committed
80
    },
Fatih Acet's avatar
Fatih Acet committed
81
  },
82 83 84 85 86 87 88
  watch: {
    shouldShow() {
      if (!this.isNotesFetched) {
        this.fetchNotes();
      }
    },
  },
Fatih Acet's avatar
Fatih Acet committed
89 90 91 92
  created() {
    this.setNotesData(this.notesData);
    this.setNoteableData(this.noteableData);
    this.setUserData(this.userData);
Felipe Artur's avatar
Felipe Artur committed
93
    this.setTargetNoteHash(getLocationHash());
94
    eventHub.$once('fetchNotesData', this.fetchNotes);
Fatih Acet's avatar
Fatih Acet committed
95 96
  },
  mounted() {
97 98 99
    if (this.shouldShow) {
      this.fetchNotes();
    }
100

101
    const { parentElement } = this.$el;
Felipe Artur's avatar
Felipe Artur committed
102
    if (parentElement && parentElement.classList.contains('js-vue-notes-event')) {
Fatih Acet's avatar
Fatih Acet committed
103 104 105 106 107 108
      parentElement.addEventListener('toggleAward', event => {
        const { awardName, noteId } = event.detail;
        this.actionToggleAward({ awardName, noteId });
      });
    }
  },
109
  updated() {
Tim Zallmann's avatar
Tim Zallmann committed
110 111 112 113
    this.$nextTick(() => {
      highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member'));
      initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
    });
114
  },
Fatih Acet's avatar
Fatih Acet committed
115
  methods: {
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    ...mapActions([
      'setLoadingState',
      'fetchDiscussions',
      'poll',
      'toggleAward',
      'scrollToNoteIfNeeded',
      'setNotesData',
      'setNoteableData',
      'setUserData',
      'setLastFetchedAt',
      'setTargetNoteHash',
      'toggleDiscussion',
      'setNotesFetchedState',
      'expandDiscussion',
      'startTaskList',
    ]),
Fatih Acet's avatar
Fatih Acet committed
132
    fetchNotes() {
133 134 135 136
      if (this.isFetching) return null;

      this.isFetching = true;

137
      return this.fetchDiscussions({ path: this.getNotesDataByProp('discussionsPath') })
Felipe Artur's avatar
Felipe Artur committed
138 139 140
        .then(() => {
          this.initPolling();
        })
Fatih Acet's avatar
Fatih Acet committed
141
        .then(() => {
142
          this.setLoadingState(false);
143
          this.setNotesFetchedState(true);
144
          eventHub.$emit('fetchedNotesData');
145
          this.isFetching = false;
Fatih Acet's avatar
Fatih Acet committed
146 147
        })
        .then(() => this.$nextTick())
148
        .then(() => this.startTaskList())
Fatih Acet's avatar
Fatih Acet committed
149 150
        .then(() => this.checkLocationHash())
        .catch(() => {
151
          this.setLoadingState(false);
152
          this.setNotesFetchedState(true);
Felipe Artur's avatar
Felipe Artur committed
153
          Flash('Something went wrong while fetching comments. Please try again.');
Fatih Acet's avatar
Fatih Acet committed
154 155 156 157 158 159
        });
    },
    initPolling() {
      if (this.isPollingInitialized) {
        return;
      }
160

Fatih Acet's avatar
Fatih Acet committed
161
      this.setLastFetchedAt(this.getNotesDataByProp('lastFetchedAt'));
162

Fatih Acet's avatar
Fatih Acet committed
163 164 165 166 167
      this.poll();
      this.isPollingInitialized = true;
    },
    checkLocationHash() {
      const hash = getLocationHash();
Felipe Artur's avatar
Felipe Artur committed
168
      const noteId = hash && hash.replace(/^note_/, '');
Fatih Acet's avatar
Fatih Acet committed
169

Felipe Artur's avatar
Felipe Artur committed
170
      if (noteId) {
171 172 173 174 175
        const discussion = this.discussions.find(d => d.notes.some(({ id }) => id === noteId));

        if (discussion) {
          this.expandDiscussion({ discussionId: discussion.id });
        }
Fatih Acet's avatar
Fatih Acet committed
176
      }
177
    },
Fatih Acet's avatar
Fatih Acet committed
178
  },
179
  systemNote: constants.SYSTEM_NOTE,
Fatih Acet's avatar
Fatih Acet committed
180
};
181 182 183
</script>

<template>
Mike Greiling's avatar
Mike Greiling committed
184 185
  <div v-show="shouldShow" id="notes">
    <ul id="notes-list" class="notes main-notes-list timeline">
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
      <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">
          <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]" />
        </template>
        <noteable-discussion
          v-else
          :key="discussion.id"
          :discussion="discussion"
          :render-diff-file="true"
        />
      </template>
211
    </ul>
212

213
    <comment-form
214
      v-if="!commentsDisabled"
215
      :noteable-type="noteableType"
216
      :markdown-version="markdownVersion"
217
    />
218 219
  </div>
</template>