IssueDiscussionsRefactor: Implement polling mechanism.

This commit is contained in:
Fatih Acet 2017-06-29 18:59:22 +03:00
parent 17d67a989b
commit 15f3362d34
5 changed files with 71 additions and 8 deletions

View file

@ -133,13 +133,13 @@ export default {
ref="textarea" ref="textarea"
slot="textarea" slot="textarea"
placeholder="Write a comment or drag your files here..." placeholder="Write a comment or drag your files here..."
@keydown.meta.enter="handleSave"> @keydown.meta.enter="handleSave()">
</textarea> </textarea>
</markdown-field> </markdown-field>
<div class="note-form-actions clearfix"> <div class="note-form-actions clearfix">
<div class="pull-left btn-group append-right-10 comment-type-dropdown js-comment-type-dropdown"> <div class="pull-left btn-group append-right-10 comment-type-dropdown js-comment-type-dropdown">
<input <input
@click="handleSave" @click="handleSave()"
:disabled="!note.length" :disabled="!note.length"
:value="commentButtonTitle" :value="commentButtonTitle"
class="btn btn-nr btn-create comment-btn js-comment-button js-comment-submit-button" class="btn btn-nr btn-create comment-btn js-comment-button js-comment-submit-button"

View file

@ -39,14 +39,27 @@ export default {
}, },
}, },
mounted() { mounted() {
const path = this.$el.parentNode.dataset.discussionsPath; const { discussionsPath, notesPath, lastFetchedAt } = this.$el.parentNode.dataset;
this.$store.dispatch('fetchNotes', path) this.$store.dispatch('fetchNotes', discussionsPath)
.then(() => { .then(() => {
this.isLoading = false; this.isLoading = false;
}) })
.catch(() => { .catch(() => {
new Flash('Something went wrong while fetching issue comments. Please try again.'); // eslint-disable-line 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);
}, },
}; };
</script> </script>

View file

@ -19,4 +19,13 @@ export default {
createNewNote(endpoint, data) { createNewNote(endpoint, data) {
return Vue.http.post(endpoint, data, { emulateJSON: true }); 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);
},
}; };

View file

@ -15,7 +15,7 @@ const getters = {
}; };
const mutations = { const mutations = {
setNotes(storeState, notes) { setInitialNotes(storeState, notes) {
storeState.notes = notes; storeState.notes = notes;
}, },
toggleDiscussion(storeState, { discussionId }) { toggleDiscussion(storeState, { discussionId }) {
@ -40,7 +40,9 @@ const mutations = {
addNewReplyToDiscussion(storeState, note) { addNewReplyToDiscussion(storeState, note) {
const noteObj = findNoteObjectById(storeState.notes, note.discussion_id); const noteObj = findNoteObjectById(storeState.notes, note.discussion_id);
noteObj.notes.push(note); if (noteObj) {
noteObj.notes.push(note);
}
}, },
updateNote(storeState, note) { updateNote(storeState, note) {
const noteObj = findNoteObjectById(storeState.notes, note.discussion_id); const noteObj = findNoteObjectById(storeState.notes, note.discussion_id);
@ -72,7 +74,7 @@ const actions = {
.fetchNotes(path) .fetchNotes(path)
.then(res => res.json()) .then(res => res.json())
.then((res) => { .then((res) => {
context.commit('setNotes', res); context.commit('setInitialNotes', res);
}); });
}, },
deleteNote(context, note) { deleteNote(context, note) {
@ -114,6 +116,45 @@ const actions = {
return res; 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 { export default {

View file

@ -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 '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' = 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 #js-notes
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'common_vue'