Commit a688cdc3 authored by Phil Hughes's avatar Phil Hughes

Updated diffs notes app to use paginated notes

This removes the need to use discussions.json
and also removes calls to this endpoint.
Instead we always use the notes endpoint and populate
the discussions from this endpoint.

https://gitlab.com/gitlab-org/gitlab/-/issues/209784
parent c8141f4c
...@@ -4,6 +4,7 @@ import OrderedLayout from '~/vue_shared/components/ordered_layout.vue'; ...@@ -4,6 +4,7 @@ import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user'; import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
import { __ } from '~/locale'; import { __ } from '~/locale';
import initUserPopovers from '~/user_popovers'; import initUserPopovers from '~/user_popovers';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility'; import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility';
import { deprecatedCreateFlash as Flash } from '../../flash'; import { deprecatedCreateFlash as Flash } from '../../flash';
import * as constants from '../constants'; import * as constants from '../constants';
...@@ -30,6 +31,7 @@ export default { ...@@ -30,6 +31,7 @@ export default {
discussionFilterNote, discussionFilterNote,
OrderedLayout, OrderedLayout,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
noteableData: { noteableData: {
type: Object, type: Object,
...@@ -57,7 +59,6 @@ export default { ...@@ -57,7 +59,6 @@ export default {
}, },
data() { data() {
return { return {
isFetching: false,
currentFilter: null, currentFilter: null,
}; };
}, },
...@@ -68,6 +69,7 @@ export default { ...@@ -68,6 +69,7 @@ export default {
'convertedDisscussionIds', 'convertedDisscussionIds',
'getNotesDataByProp', 'getNotesDataByProp',
'isLoading', 'isLoading',
'isFetching',
'commentsDisabled', 'commentsDisabled',
'getNoteableData', 'getNoteableData',
'userCanReply', 'userCanReply',
...@@ -153,6 +155,7 @@ export default { ...@@ -153,6 +155,7 @@ export default {
}, },
methods: { methods: {
...mapActions([ ...mapActions([
'setFetchingState',
'setLoadingState', 'setLoadingState',
'fetchDiscussions', 'fetchDiscussions',
'poll', 'poll',
...@@ -183,7 +186,11 @@ export default { ...@@ -183,7 +186,11 @@ export default {
fetchNotes() { fetchNotes() {
if (this.isFetching) return null; if (this.isFetching) return null;
this.isFetching = true; this.setFetchingState(true);
if (this.glFeatures.paginatedNotes) {
return this.initPolling();
}
return this.fetchDiscussions(this.getFetchDiscussionsConfig()) return this.fetchDiscussions(this.getFetchDiscussionsConfig())
.then(this.initPolling) .then(this.initPolling)
...@@ -191,7 +198,7 @@ export default { ...@@ -191,7 +198,7 @@ export default {
this.setLoadingState(false); this.setLoadingState(false);
this.setNotesFetchedState(true); this.setNotesFetchedState(true);
eventHub.$emit('fetchedNotesData'); eventHub.$emit('fetchedNotesData');
this.isFetching = false; this.setFetchingState(false);
}) })
.then(this.$nextTick) .then(this.$nextTick)
.then(this.startTaskList) .then(this.startTaskList)
......
...@@ -15,6 +15,7 @@ import loadAwardsHandler from '../../awards_handler'; ...@@ -15,6 +15,7 @@ import loadAwardsHandler from '../../awards_handler';
import sidebarTimeTrackingEventHub from '../../sidebar/event_hub'; import sidebarTimeTrackingEventHub from '../../sidebar/event_hub';
import { isInViewport, scrollToElement, isInMRPage } from '../../lib/utils/common_utils'; import { isInViewport, scrollToElement, isInMRPage } from '../../lib/utils/common_utils';
import { mergeUrlParams } from '../../lib/utils/url_utility'; import { mergeUrlParams } from '../../lib/utils/url_utility';
import eventHub from '../event_hub';
import mrWidgetEventHub from '../../vue_merge_request_widget/event_hub'; import mrWidgetEventHub from '../../vue_merge_request_widget/event_hub';
import * as utils from './utils'; import * as utils from './utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
...@@ -420,11 +421,21 @@ export const saveNote = ({ commit, dispatch }, noteData) => { ...@@ -420,11 +421,21 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
.catch(processErrors); .catch(processErrors);
}; };
export const setFetchingState = ({ commit }, fetchingState) =>
commit(types.SET_NOTES_FETCHING_STATE, fetchingState);
const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => { const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => {
if (state.isResolvingDiscussion) { if (state.isResolvingDiscussion) {
return null; return null;
} }
if (window.gon?.features?.paginatedNotes && !resp.more && state.isFetching) {
eventHub.$emit('fetchedNotesData');
dispatch('setFetchingState', false);
dispatch('setNotesFetchedState', true);
dispatch('setLoadingState', false);
}
if (resp.notes?.length) { if (resp.notes?.length) {
dispatch('updateOrCreateNotes', resp.notes); dispatch('updateOrCreateNotes', resp.notes);
dispatch('startTaskList'); dispatch('startTaskList');
......
...@@ -48,6 +48,8 @@ export const persistSortOrder = (state) => state.persistSortOrder; ...@@ -48,6 +48,8 @@ export const persistSortOrder = (state) => state.persistSortOrder;
export const timelineEnabled = (state) => state.isTimelineEnabled; export const timelineEnabled = (state) => state.isTimelineEnabled;
export const isFetching = (state) => state.isFetching;
export const isLoading = (state) => state.isLoading; export const isLoading = (state) => state.isLoading;
export const getNotesDataByProp = (state) => (prop) => state.notesData[prop]; export const getNotesDataByProp = (state) => (prop) => state.notesData[prop];
......
...@@ -47,6 +47,7 @@ export default () => ({ ...@@ -47,6 +47,7 @@ export default () => ({
unresolvedDiscussionsCount: 0, unresolvedDiscussionsCount: 0,
descriptionVersions: {}, descriptionVersions: {},
isTimelineEnabled: false, isTimelineEnabled: false,
isFetching: false,
}, },
actions, actions,
getters, getters,
......
...@@ -14,6 +14,7 @@ export const UPDATE_NOTE = 'UPDATE_NOTE'; ...@@ -14,6 +14,7 @@ export const UPDATE_NOTE = 'UPDATE_NOTE';
export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION'; export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION';
export const UPDATE_DISCUSSION_POSITION = 'UPDATE_DISCUSSION_POSITION'; export const UPDATE_DISCUSSION_POSITION = 'UPDATE_DISCUSSION_POSITION';
export const SET_DISCUSSION_DIFF_LINES = 'SET_DISCUSSION_DIFF_LINES'; export const SET_DISCUSSION_DIFF_LINES = 'SET_DISCUSSION_DIFF_LINES';
export const SET_NOTES_FETCHING_STATE = 'SET_NOTES_FETCHING_STATE';
export const SET_NOTES_FETCHED_STATE = 'SET_NOTES_FETCHED_STATE'; export const SET_NOTES_FETCHED_STATE = 'SET_NOTES_FETCHED_STATE';
export const SET_NOTES_LOADING_STATE = 'SET_NOTES_LOADING_STATE'; export const SET_NOTES_LOADING_STATE = 'SET_NOTES_LOADING_STATE';
export const DISABLE_COMMENTS = 'DISABLE_COMMENTS'; export const DISABLE_COMMENTS = 'DISABLE_COMMENTS';
......
...@@ -323,6 +323,10 @@ export default { ...@@ -323,6 +323,10 @@ export default {
state.isLoading = value; state.isLoading = value;
}, },
[types.SET_NOTES_FETCHING_STATE](state, value) {
state.isFetching = value;
},
[types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) { [types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId); const discussion = utils.findNoteObjectById(state.discussions, discussionId);
......
...@@ -42,6 +42,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -42,6 +42,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:codequality_mr_diff, @project) push_frontend_feature_flag(:codequality_mr_diff, @project)
push_frontend_feature_flag(:suggestions_custom_commit, @project) push_frontend_feature_flag(:suggestions_custom_commit, @project)
push_frontend_feature_flag(:local_file_reviews, default_enabled: :yaml) push_frontend_feature_flag(:local_file_reviews, default_enabled: :yaml)
push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml)
record_experiment_user(:invite_members_version_a) record_experiment_user(:invite_members_version_a)
record_experiment_user(:invite_members_version_b) record_experiment_user(:invite_members_version_b)
......
...@@ -175,7 +175,9 @@ module NotesHelper ...@@ -175,7 +175,9 @@ module NotesHelper
end end
end end
def notes_data(issuable) def notes_data(issuable, start_at_zero = false)
initial_last_fetched_at = start_at_zero ? 0 : Time.current.to_i * ::Gitlab::UpdatedNotesPaginator::MICROSECOND
data = { data = {
discussionsPath: discussions_path(issuable), discussionsPath: discussions_path(issuable),
registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'), registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'),
...@@ -186,7 +188,7 @@ module NotesHelper ...@@ -186,7 +188,7 @@ module NotesHelper
reopenPath: reopen_issuable_path(issuable), reopenPath: reopen_issuable_path(issuable),
notesPath: notes_url, notesPath: notes_url,
prerenderedNotesCount: issuable.capped_notes_count(MAX_PRERENDERED_NOTES), prerenderedNotesCount: issuable.capped_notes_count(MAX_PRERENDERED_NOTES),
lastFetchedAt: Time.now.to_i * ::Gitlab::UpdatedNotesPaginator::MICROSECOND lastFetchedAt: initial_last_fetched_at
} }
if issuable.is_a?(MergeRequest) if issuable.is_a?(MergeRequest)
......
...@@ -56,10 +56,13 @@ ...@@ -56,10 +56,13 @@
= render "projects/merge_requests/widget" = render "projects/merge_requests/widget"
= render "projects/merge_requests/awards_block" = render "projects/merge_requests/awards_block"
- if mr_action === "show" - if mr_action === "show"
- if Feature.enabled?(:paginated_notes, @project)
- add_page_startup_api_call notes_url
- else
- add_page_startup_api_call discussions_path(@merge_request) - add_page_startup_api_call discussions_path(@merge_request)
- add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, format: :json) - add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, format: :json)
- add_page_startup_api_call cached_widget_project_json_merge_request_path(@project, @merge_request, format: :json) - add_page_startup_api_call cached_widget_project_json_merge_request_path(@project, @merge_request, format: :json)
#js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request).to_json, #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request, Feature.enabled?(:paginated_notes, @project)).to_json,
noteable_data: serialize_issuable(@merge_request, serializer: 'noteable'), noteable_data: serialize_issuable(@merge_request, serializer: 'noteable'),
noteable_type: 'MergeRequest', noteable_type: 'MergeRequest',
target_type: 'merge_request', target_type: 'merge_request',
......
...@@ -294,6 +294,40 @@ describe('Actions Notes Store', () => { ...@@ -294,6 +294,40 @@ describe('Actions Notes Store', () => {
], ],
)); ));
}); });
describe('paginated notes feature flag enabled', () => {
const lastFetchedAt = '12358';
beforeEach(() => {
window.gon = { features: { paginatedNotes: true } };
axiosMock.onGet(notesDataMock.notesPath).replyOnce(200, {
notes: discussionMock.notes,
more: false,
last_fetched_at: lastFetchedAt,
});
});
afterEach(() => {
window.gon = null;
});
it('should dispatch setFetchingState, setNotesFetchedState, setLoadingState, updateOrCreateNotes, startTaskList and commit SET_LAST_FETCHED_AT', () => {
return testAction(
actions.fetchData,
null,
{ notesData: notesDataMock, isFetching: true },
[{ type: 'SET_LAST_FETCHED_AT', payload: lastFetchedAt }],
[
{ type: 'setFetchingState', payload: false },
{ type: 'setNotesFetchedState', payload: true },
{ type: 'setLoadingState', payload: false },
{ type: 'updateOrCreateNotes', payload: discussionMock.notes },
{ type: 'startTaskList' },
],
);
});
});
}); });
describe('poll', () => { describe('poll', () => {
...@@ -1355,4 +1389,17 @@ describe('Actions Notes Store', () => { ...@@ -1355,4 +1389,17 @@ describe('Actions Notes Store', () => {
); );
}); });
}); });
describe('setFetchingState', () => {
it('commits SET_NOTES_FETCHING_STATE', (done) => {
testAction(
actions.setFetchingState,
true,
null,
[{ type: mutationTypes.SET_NOTES_FETCHING_STATE, payload: true }],
[],
done,
);
});
});
}); });
...@@ -316,4 +316,15 @@ RSpec.describe NotesHelper do ...@@ -316,4 +316,15 @@ RSpec.describe NotesHelper do
end end
end end
end end
describe '#notes_data' do
let(:issue) { create(:issue, project: project) }
it 'sets last_fetched_at to 0 when start_at_zero is true' do
@project = project
@noteable = issue
expect(helper.notes_data(issue, true)[:lastFetchedAt]).to eq(0)
end
end
end end
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