From 15f3362d343ab7ea51402b81c339bc1bd25fa9eb Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Thu, 29 Jun 2017 18:59:22 +0300 Subject: [PATCH] IssueDiscussionsRefactor: Implement polling mechanism. --- .../notes/components/issue_comment_form.vue | 4 +- .../notes/components/issue_notes.vue | 17 ++++++- .../notes/services/issue_notes_service.js | 9 ++++ .../notes/stores/issue_notes_store.js | 47 +++++++++++++++++-- .../projects/issues/_discussion.html.haml | 2 +- 5 files changed, 71 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/notes/components/issue_comment_form.vue b/app/assets/javascripts/notes/components/issue_comment_form.vue index 42bac8338ff..72d9bc7451a 100644 --- a/app/assets/javascripts/notes/components/issue_comment_form.vue +++ b/app/assets/javascripts/notes/components/issue_comment_form.vue @@ -133,13 +133,13 @@ export default { ref="textarea" slot="textarea" placeholder="Write a comment or drag your files here..." - @keydown.meta.enter="handleSave"> + @keydown.meta.enter="handleSave()">
{ this.isLoading = false; }) .catch(() => { new Flash('Something went wrong while fetching issue comments. Please try again.'); // eslint-disable-line }); + + const options = { + endpoint: `${notesPath}?full_data=1`, + lastFetchedAt, + }; + + // FIXME: @fatihacet Implement real polling mechanism + setInterval(() => { + this.$store.dispatch('poll', options) + .then((res) => { + options.lastFetchedAt = res.last_fetched_at; + }); + }, 6000); }, }; diff --git a/app/assets/javascripts/notes/services/issue_notes_service.js b/app/assets/javascripts/notes/services/issue_notes_service.js index c8b108d8a90..6400e607d8a 100644 --- a/app/assets/javascripts/notes/services/issue_notes_service.js +++ b/app/assets/javascripts/notes/services/issue_notes_service.js @@ -19,4 +19,13 @@ export default { createNewNote(endpoint, data) { return Vue.http.post(endpoint, data, { emulateJSON: true }); }, + poll(endpoint, lastFetchedAt) { + const options = { + headers: { + 'X-Last-Fetched-At': lastFetchedAt, + } + }; + + return Vue.http.get(endpoint, options); + }, }; diff --git a/app/assets/javascripts/notes/stores/issue_notes_store.js b/app/assets/javascripts/notes/stores/issue_notes_store.js index 5a9c3aaad22..0eec355975e 100644 --- a/app/assets/javascripts/notes/stores/issue_notes_store.js +++ b/app/assets/javascripts/notes/stores/issue_notes_store.js @@ -15,7 +15,7 @@ const getters = { }; const mutations = { - setNotes(storeState, notes) { + setInitialNotes(storeState, notes) { storeState.notes = notes; }, toggleDiscussion(storeState, { discussionId }) { @@ -40,7 +40,9 @@ const mutations = { addNewReplyToDiscussion(storeState, note) { const noteObj = findNoteObjectById(storeState.notes, note.discussion_id); - noteObj.notes.push(note); + if (noteObj) { + noteObj.notes.push(note); + } }, updateNote(storeState, note) { const noteObj = findNoteObjectById(storeState.notes, note.discussion_id); @@ -72,7 +74,7 @@ const actions = { .fetchNotes(path) .then(res => res.json()) .then((res) => { - context.commit('setNotes', res); + context.commit('setInitialNotes', res); }); }, deleteNote(context, note) { @@ -114,6 +116,45 @@ const actions = { return res; }); }, + poll(context, data) { + const { endpoint, lastFetchedAt } = data; + + return service + .poll(endpoint, lastFetchedAt) + .then(res => res.json()) + .then((res) => { + if (res.notes.length) { + const notesById = {}; + + // Simple lookup object to check whether we have a discussion id already in our store + context.state.notes.forEach((note) => { + note.notes.forEach((n) => { + notesById[n.id] = true; + }); + }); + + res.notes.forEach((note) => { + if (notesById[note.id]) { + context.commit('updateNote', note); + } else { + if (note.type === 'DiscussionNote') { + const discussion = findNoteObjectById(context.state.notes, note.discussion_id); + + if (discussion) { + context.commit('addNewReplyToDiscussion', note); + } else { + context.commit('addNewNote', note); + } + } else { + context.commit('addNewNote', note); + } + } + }); + } + + return res; + }); + }, }; export default { diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index 8e4b9f8910a..ab0534f1fb0 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -3,7 +3,7 @@ = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, format: 'json'), data: {original_text: "Reopen issue", alternative_text: "Comment & reopen issue"}, class: "btn btn-nr btn-reopen btn-comment js-note-target-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, format: 'json'), data: {original_text: "Close issue", alternative_text: "Comment & close issue"}, class: "btn btn-nr btn-close btn-comment js-note-target-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' -%section{ data: { discussions_path: discussions_namespace_project_issue_path(@project.namespace, @project, @issue, format: :json), new_session_path: new_session_path(:user, redirect_to_referer: 'yes') } } +%section{ data: { discussions_path: discussions_namespace_project_issue_path(@project.namespace, @project, @issue, format: :json), new_session_path: new_session_path(:user, redirect_to_referer: 'yes'), notes_path: notes_url, last_fetched_at: Time.now.to_i } } #js-notes - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue'