Commit f60734d3 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu Committed by Phil Hughes

Fix polling for replies to individual notes

Sets `updated_at` so that the transformed note is returned together with
the new reply
parent 4b2ba1a7
...@@ -93,6 +93,7 @@ export default { ...@@ -93,6 +93,7 @@ export default {
}, },
computed: { computed: {
...mapGetters([ ...mapGetters([
'convertedDisscussionIds',
'getNoteableData', 'getNoteableData',
'nextUnresolvedDiscussionId', 'nextUnresolvedDiscussionId',
'unresolvedDiscussionsCount', 'unresolvedDiscussionsCount',
...@@ -301,6 +302,10 @@ export default { ...@@ -301,6 +302,10 @@ export default {
note: { note: noteText }, note: { note: noteText },
}; };
if (this.convertedDisscussionIds.includes(this.discussion.id)) {
postData.return_discussion = true;
}
if (this.discussion.for_commit) { if (this.discussion.for_commit) {
postData.note_project_id = this.discussion.project_id; postData.note_project_id = this.discussion.project_id;
} }
......
...@@ -60,6 +60,7 @@ export default { ...@@ -60,6 +60,7 @@ export default {
...mapGetters([ ...mapGetters([
'isNotesFetched', 'isNotesFetched',
'discussions', 'discussions',
'convertedDisscussionIds',
'getNotesDataByProp', 'getNotesDataByProp',
'isLoading', 'isLoading',
'commentsDisabled', 'commentsDisabled',
...@@ -193,7 +194,9 @@ export default { ...@@ -193,7 +194,9 @@ export default {
/> />
<placeholder-note v-else :key="discussion.id" :note="discussion.notes[0]" /> <placeholder-note v-else :key="discussion.id" :note="discussion.notes[0]" />
</template> </template>
<template v-else-if="discussion.individual_note"> <template
v-else-if="discussion.individual_note && !convertedDisscussionIds.includes(discussion.id)"
>
<system-note <system-note
v-if="discussion.notes[0].system" v-if="discussion.notes[0].system"
:key="discussion.id" :key="discussion.id"
......
...@@ -83,12 +83,44 @@ export const updateNote = ({ commit, dispatch }, { endpoint, note }) => ...@@ -83,12 +83,44 @@ export const updateNote = ({ commit, dispatch }, { endpoint, note }) =>
dispatch('startTaskList'); dispatch('startTaskList');
}); });
export const replyToDiscussion = ({ commit }, { endpoint, data }) => export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes) => {
const { notesById } = getters;
notes.forEach(note => {
if (notesById[note.id]) {
commit(types.UPDATE_NOTE, note);
} else if (note.type === constants.DISCUSSION_NOTE || note.type === constants.DIFF_NOTE) {
const discussion = utils.findNoteObjectById(state.discussions, note.discussion_id);
if (discussion) {
commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note);
} else if (note.type === constants.DIFF_NOTE) {
dispatch('fetchDiscussions', { path: state.notesData.discussionsPath });
} else {
commit(types.ADD_NEW_NOTE, note);
}
} else {
commit(types.ADD_NEW_NOTE, note);
}
});
};
export const replyToDiscussion = ({ commit, state, getters, dispatch }, { endpoint, data }) =>
service service
.replyToDiscussion(endpoint, data) .replyToDiscussion(endpoint, data)
.then(res => res.json()) .then(res => res.json())
.then(res => { .then(res => {
if (res.discussion) {
commit(types.UPDATE_DISCUSSION, res.discussion);
updateOrCreateNotes({ commit, state, getters, dispatch }, res.discussion.notes);
dispatch('updateMergeRequestWidget');
dispatch('startTaskList');
dispatch('updateResolvableDiscussonsCounts');
} else {
commit(types.ADD_NEW_REPLY_TO_DISCUSSION, res); commit(types.ADD_NEW_REPLY_TO_DISCUSSION, res);
}
return res; return res;
}); });
...@@ -262,25 +294,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => { ...@@ -262,25 +294,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => { const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => {
if (resp.notes && resp.notes.length) { if (resp.notes && resp.notes.length) {
const { notesById } = getters; updateOrCreateNotes({ commit, state, getters, dispatch }, resp.notes);
resp.notes.forEach(note => {
if (notesById[note.id]) {
commit(types.UPDATE_NOTE, note);
} else if (note.type === constants.DISCUSSION_NOTE || note.type === constants.DIFF_NOTE) {
const discussion = utils.findNoteObjectById(state.discussions, note.discussion_id);
if (discussion) {
commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note);
} else if (note.type === constants.DIFF_NOTE) {
dispatch('fetchDiscussions', { path: state.notesData.discussionsPath });
} else {
commit(types.ADD_NEW_NOTE, note);
}
} else {
commit(types.ADD_NEW_NOTE, note);
}
});
dispatch('startTaskList'); dispatch('startTaskList');
} }
......
...@@ -4,6 +4,8 @@ import { collapseSystemNotes } from './collapse_utils'; ...@@ -4,6 +4,8 @@ import { collapseSystemNotes } from './collapse_utils';
export const discussions = state => collapseSystemNotes(state.discussions); export const discussions = state => collapseSystemNotes(state.discussions);
export const convertedDisscussionIds = state => state.convertedDisscussionIds;
export const targetNoteHash = state => state.targetNoteHash; export const targetNoteHash = state => state.targetNoteHash;
export const getNotesData = state => state.notesData; export const getNotesData = state => state.notesData;
......
...@@ -5,6 +5,7 @@ import mutations from '../mutations'; ...@@ -5,6 +5,7 @@ import mutations from '../mutations';
export default () => ({ export default () => ({
state: { state: {
discussions: [], discussions: [],
convertedDisscussionIds: [],
targetNoteHash: null, targetNoteHash: null,
lastFetchedAt: null, lastFetchedAt: null,
......
...@@ -266,7 +266,7 @@ export default { ...@@ -266,7 +266,7 @@ export default {
}, },
[types.CONVERT_TO_DISCUSSION](state, discussionId) { [types.CONVERT_TO_DISCUSSION](state, discussionId) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId); const convertedDisscussionIds = [...state.convertedDisscussionIds, discussionId];
Object.assign(discussion, { individual_note: false }); Object.assign(state, { convertedDisscussionIds });
}, },
}; };
...@@ -84,7 +84,10 @@ export default ( ...@@ -84,7 +84,10 @@ export default (
done(); done();
}; };
const result = action({ commit, state, dispatch, rootState: state, rootGetters: state }, payload); const result = action(
{ commit, state, dispatch, rootState: state, rootGetters: state, getters: state },
payload,
);
return new Promise(resolve => { return new Promise(resolve => {
setImmediate(resolve); setImmediate(resolve);
......
import Vue from 'vue'; import Vue from 'vue';
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
import { TEST_HOST } from 'spec/test_constants';
import { headersInterceptor } from 'spec/helpers/vue_resource_helper'; import { headersInterceptor } from 'spec/helpers/vue_resource_helper';
import * as actions from '~/notes/stores/actions'; import * as actions from '~/notes/stores/actions';
import * as mutationTypes from '~/notes/stores/mutation_types';
import * as notesConstants from '~/notes/constants';
import createStore from '~/notes/stores'; import createStore from '~/notes/stores';
import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub'; import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub';
import testAction from '../../helpers/vuex_action_helper'; import testAction from '../../helpers/vuex_action_helper';
...@@ -599,4 +602,139 @@ describe('Actions Notes Store', () => { ...@@ -599,4 +602,139 @@ describe('Actions Notes Store', () => {
); );
}); });
}); });
describe('updateOrCreateNotes', () => {
let commit;
let dispatch;
let state;
beforeEach(() => {
commit = jasmine.createSpy('commit');
dispatch = jasmine.createSpy('dispatch');
state = {};
});
afterEach(() => {
commit.calls.reset();
dispatch.calls.reset();
});
it('Updates existing note', () => {
const note = { id: 1234 };
const getters = { notesById: { 1234: note } };
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [note]);
expect(commit.calls.allArgs()).toEqual([[mutationTypes.UPDATE_NOTE, note]]);
});
it('Creates a new note if none exisits', () => {
const note = { id: 1234 };
const getters = { notesById: {} };
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [note]);
expect(commit.calls.allArgs()).toEqual([[mutationTypes.ADD_NEW_NOTE, note]]);
});
describe('Discussion notes', () => {
let note;
let getters;
beforeEach(() => {
note = { id: 1234 };
getters = { notesById: {} };
});
it('Adds a reply to an existing discussion', () => {
state = { discussions: [note] };
const discussionNote = {
...note,
type: notesConstants.DISCUSSION_NOTE,
discussion_id: 1234,
};
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [discussionNote]);
expect(commit.calls.allArgs()).toEqual([
[mutationTypes.ADD_NEW_REPLY_TO_DISCUSSION, discussionNote],
]);
});
it('fetches discussions for diff notes', () => {
state = { discussions: [], notesData: { discussionsPath: 'Hello world' } };
const diffNote = { ...note, type: notesConstants.DIFF_NOTE, discussion_id: 1234 };
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [diffNote]);
expect(dispatch.calls.allArgs()).toEqual([
['fetchDiscussions', { path: state.notesData.discussionsPath }],
]);
});
it('Adds a new note', () => {
state = { discussions: [] };
const discussionNote = {
...note,
type: notesConstants.DISCUSSION_NOTE,
discussion_id: 1234,
};
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [discussionNote]);
expect(commit.calls.allArgs()).toEqual([[mutationTypes.ADD_NEW_NOTE, discussionNote]]);
});
});
});
describe('replyToDiscussion', () => {
let res = { discussion: { notes: [] } };
const payload = { endpoint: TEST_HOST, data: {} };
const interceptor = (request, next) => {
next(
request.respondWith(JSON.stringify(res), {
status: 200,
}),
);
};
beforeEach(() => {
Vue.http.interceptors.push(interceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
});
it('updates discussion if response contains disussion', done => {
testAction(
actions.replyToDiscussion,
payload,
{
notesById: {},
},
[{ type: mutationTypes.UPDATE_DISCUSSION, payload: res.discussion }],
[
{ type: 'updateMergeRequestWidget' },
{ type: 'startTaskList' },
{ type: 'updateResolvableDiscussonsCounts' },
],
done,
);
});
it('adds a reply to a discussion', done => {
res = {};
testAction(
actions.replyToDiscussion,
payload,
{
notesById: {},
},
[{ type: mutationTypes.ADD_NEW_REPLY_TO_DISCUSSION, payload: res }],
[],
done,
);
});
});
}); });
...@@ -527,17 +527,13 @@ describe('Notes Store mutations', () => { ...@@ -527,17 +527,13 @@ describe('Notes Store mutations', () => {
id: 42, id: 42,
individual_note: true, individual_note: true,
}; };
state = { discussions: [discussion] }; state = { convertedDisscussionIds: [] };
}); });
it('toggles individual_note', () => { it('adds a disucssion to convertedDisscussionIds', () => {
mutations.CONVERT_TO_DISCUSSION(state, discussion.id); mutations.CONVERT_TO_DISCUSSION(state, discussion.id);
expect(discussion.individual_note).toBe(false); expect(state.convertedDisscussionIds).toContain(discussion.id);
});
it('throws if discussion was not found', () => {
expect(() => mutations.CONVERT_TO_DISCUSSION(state, 99)).toThrow();
}); });
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment