Commit 5b23a0c3 authored by Clement Ho's avatar Clement Ho

Merge branch...

Merge branch '30497-race-condition-in-discussions-json-request-could-lead-to-notes-showing-inconsistently-with-selected-notes-filter' into 'master'

Fix notes race condition when linking to specific note

See merge request gitlab-org/gitlab!17777
parents e5c08fcb 96f6ba65
...@@ -87,6 +87,14 @@ export function getLocationHash(url = window.location.href) { ...@@ -87,6 +87,14 @@ export function getLocationHash(url = window.location.href) {
return hashIndex === -1 ? null : url.substring(hashIndex + 1); return hashIndex === -1 ? null : url.substring(hashIndex + 1);
} }
/**
* Returns a boolean indicating whether the URL hash contains the given string value
*/
export function doesHashExistInUrl(hashName) {
const hash = getLocationHash();
return hash && hash.includes(hashName);
}
/** /**
* Apply the fragment to the given url by returning a new url string that includes * Apply the fragment to the given url by returning a new url string that includes
* the fragment. If the given url already contains a fragment, the original fragment * the fragment. If the given url already contains a fragment, the original fragment
......
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import { getLocationHash } from '../../lib/utils/url_utility'; import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { import {
DISCUSSION_FILTERS_DEFAULT_VALUE, DISCUSSION_FILTERS_DEFAULT_VALUE,
HISTORY_ONLY_FILTER_VALUE, HISTORY_ONLY_FILTER_VALUE,
DISCUSSION_TAB_LABEL, DISCUSSION_TAB_LABEL,
DISCUSSION_FILTER_TYPES, DISCUSSION_FILTER_TYPES,
NOTE_UNDERSCORE,
} from '../constants'; } from '../constants';
import notesEventHub from '../event_hub'; import notesEventHub from '../event_hub';
...@@ -28,7 +29,9 @@ export default { ...@@ -28,7 +29,9 @@ export default {
}, },
data() { data() {
return { return {
currentValue: this.selectedValue, currentValue: doesHashExistInUrl(NOTE_UNDERSCORE)
? DISCUSSION_FILTERS_DEFAULT_VALUE
: this.selectedValue,
defaultValue: DISCUSSION_FILTERS_DEFAULT_VALUE, defaultValue: DISCUSSION_FILTERS_DEFAULT_VALUE,
displayFilters: true, displayFilters: true,
}; };
...@@ -50,7 +53,6 @@ export default { ...@@ -50,7 +53,6 @@ export default {
notesEventHub.$on('dropdownSelect', this.selectFilter); notesEventHub.$on('dropdownSelect', this.selectFilter);
window.addEventListener('hashchange', this.handleLocationHash); window.addEventListener('hashchange', this.handleLocationHash);
this.handleLocationHash();
}, },
mounted() { mounted() {
this.toggleCommentsForm(); this.toggleCommentsForm();
......
<script> <script>
import { __ } from '~/locale'; import { __ } from '~/locale';
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import { getLocationHash } from '../../lib/utils/url_utility'; import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility';
import Flash from '../../flash'; import Flash from '../../flash';
import * as constants from '../constants'; import * as constants from '../constants';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
...@@ -156,19 +156,17 @@ export default { ...@@ -156,19 +156,17 @@ export default {
this.isFetching = true; this.isFetching = true;
return this.fetchDiscussions({ path: this.getNotesDataByProp('discussionsPath') }) return this.fetchDiscussions(this.getFetchDiscussionsConfig())
.then(() => { .then(this.initPolling)
this.initPolling();
})
.then(() => { .then(() => {
this.setLoadingState(false); this.setLoadingState(false);
this.setNotesFetchedState(true); this.setNotesFetchedState(true);
eventHub.$emit('fetchedNotesData'); eventHub.$emit('fetchedNotesData');
this.isFetching = false; this.isFetching = false;
}) })
.then(() => this.$nextTick()) .then(this.$nextTick)
.then(() => this.startTaskList()) .then(this.startTaskList)
.then(() => this.checkLocationHash()) .then(this.checkLocationHash)
.catch(() => { .catch(() => {
this.setLoadingState(false); this.setLoadingState(false);
this.setNotesFetchedState(true); this.setNotesFetchedState(true);
...@@ -199,9 +197,20 @@ export default { ...@@ -199,9 +197,20 @@ export default {
}, },
startReplying(discussionId) { startReplying(discussionId) {
return this.convertToDiscussion(discussionId) return this.convertToDiscussion(discussionId)
.then(() => this.$nextTick()) .then(this.$nextTick)
.then(() => eventHub.$emit('startReplying', discussionId)); .then(() => eventHub.$emit('startReplying', discussionId));
}, },
getFetchDiscussionsConfig() {
const defaultConfig = { path: this.getNotesDataByProp('discussionsPath') };
if (doesHashExistInUrl(constants.NOTE_UNDERSCORE)) {
return Object.assign({}, defaultConfig, {
filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE,
persistFilter: false,
});
}
return defaultConfig;
},
}, },
systemNote: constants.SYSTEM_NOTE, systemNote: constants.SYSTEM_NOTE,
}; };
......
...@@ -8,8 +8,6 @@ export const OPENED = 'opened'; ...@@ -8,8 +8,6 @@ export const OPENED = 'opened';
export const REOPENED = 'reopened'; export const REOPENED = 'reopened';
export const CLOSED = 'closed'; export const CLOSED = 'closed';
export const MERGED = 'merged'; export const MERGED = 'merged';
export const EMOJI_THUMBSUP = 'thumbsup';
export const EMOJI_THUMBSDOWN = 'thumbsdown';
export const ISSUE_NOTEABLE_TYPE = 'issue'; export const ISSUE_NOTEABLE_TYPE = 'issue';
export const EPIC_NOTEABLE_TYPE = 'epic'; export const EPIC_NOTEABLE_TYPE = 'epic';
export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest'; export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest';
...@@ -19,6 +17,7 @@ export const DESCRIPTION_TYPE = 'changed the description'; ...@@ -19,6 +17,7 @@ export const DESCRIPTION_TYPE = 'changed the description';
export const HISTORY_ONLY_FILTER_VALUE = 2; export const HISTORY_ONLY_FILTER_VALUE = 2;
export const DISCUSSION_FILTERS_DEFAULT_VALUE = 0; export const DISCUSSION_FILTERS_DEFAULT_VALUE = 0;
export const DISCUSSION_TAB_LABEL = 'show'; export const DISCUSSION_TAB_LABEL = 'show';
export const NOTE_UNDERSCORE = 'note_';
export const NOTEABLE_TYPE_MAPPING = { export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE, Issue: ISSUE_NOTEABLE_TYPE,
......
---
title: Fix notes race condition when linking to specific note
merge_request: 17777
author:
type: fixed
...@@ -136,6 +136,24 @@ describe('URL utility', () => { ...@@ -136,6 +136,24 @@ describe('URL utility', () => {
}); });
}); });
describe('doesHashExistInUrl', () => {
it('should return true when the given string exists in the URL hash', () => {
setWindowLocation({
href: 'https://gitlab.com/gitlab-org/gitlab-test/issues/1#note_1',
});
expect(urlUtils.doesHashExistInUrl('note_')).toBe(true);
});
it('should return false when the given string does not exist in the URL hash', () => {
setWindowLocation({
href: 'https://gitlab.com/gitlab-org/gitlab-test/issues/1#note_1',
});
expect(urlUtils.doesHashExistInUrl('doesnotexist')).toBe(false);
});
});
describe('setUrlFragment', () => { describe('setUrlFragment', () => {
it('should set fragment when url has no fragment', () => { it('should set fragment when url has no fragment', () => {
const url = urlUtils.setUrlFragment('/home/feature', 'usage'); const url = urlUtils.setUrlFragment('/home/feature', 'usage');
......
...@@ -160,5 +160,28 @@ describe('DiscussionFilter component', () => { ...@@ -160,5 +160,28 @@ describe('DiscussionFilter component', () => {
done(); done();
}); });
}); });
it('fetches discussions when there is a hash', done => {
window.location.hash = `note_${discussionMock.notes[0].id}`;
vm.currentValue = discussionFiltersMock[2].value;
spyOn(vm, 'selectFilter');
vm.handleLocationHash();
vm.$nextTick(() => {
expect(vm.selectFilter).toHaveBeenCalled();
done();
});
});
it('does not fetch discussions when there is no hash', done => {
window.location.hash = '';
spyOn(vm, 'selectFilter');
vm.handleLocationHash();
vm.$nextTick(() => {
expect(vm.selectFilter).not.toHaveBeenCalled();
done();
});
});
}); });
}); });
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