2020-04-22 19:07:51 +05:30
|
|
|
import { flattenDeep, clone } from 'lodash';
|
2018-11-08 19:23:39 +05:30
|
|
|
import * as constants from '../constants';
|
|
|
|
import { collapseSystemNotes } from './collapse_utils';
|
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
export const discussions = state => {
|
|
|
|
let discussionsInState = clone(state.discussions);
|
|
|
|
// NOTE: not testing bc will be removed when backend is finished.
|
|
|
|
if (state.discussionSortOrder === constants.DESC) {
|
|
|
|
discussionsInState = discussionsInState.reverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
return collapseSystemNotes(discussionsInState);
|
|
|
|
};
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
export const convertedDisscussionIds = state => state.convertedDisscussionIds;
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
export const targetNoteHash = state => state.targetNoteHash;
|
|
|
|
|
|
|
|
export const getNotesData = state => state.notesData;
|
2018-11-08 19:23:39 +05:30
|
|
|
|
|
|
|
export const isNotesFetched = state => state.isNotesFetched;
|
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
/*
|
|
|
|
* WARNING: This is an example of an "unnecessary" getter
|
|
|
|
* more info found here: https://gitlab.com/groups/gitlab-org/-/epics/2913.
|
|
|
|
*/
|
|
|
|
|
|
|
|
export const sortDirection = state => state.discussionSortOrder;
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
export const isLoading = state => state.isLoading;
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
export const getNotesDataByProp = state => prop => state.notesData[prop];
|
|
|
|
|
|
|
|
export const getNoteableData = state => state.noteableData;
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
export const getNoteableDataByProp = state => prop => state.noteableData[prop];
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
export const userCanReply = state => Boolean(state.noteableData.current_user.can_create_note);
|
2019-07-31 22:56:46 +05:30
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
export const openState = state => state.noteableData.state;
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
export const getUserData = state => state.userData || {};
|
2018-11-08 19:23:39 +05:30
|
|
|
|
|
|
|
export const getUserDataByProp = state => prop => state.userData && state.userData[prop];
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
export const descriptionVersions = state => state.descriptionVersions;
|
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
export const notesById = state =>
|
2018-11-08 19:23:39 +05:30
|
|
|
state.discussions.reduce((acc, note) => {
|
2018-05-09 12:01:36 +05:30
|
|
|
note.notes.every(n => Object.assign(acc, { [n.id]: n }));
|
|
|
|
return acc;
|
|
|
|
}, {});
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
export const noteableType = state => {
|
|
|
|
const { ISSUE_NOTEABLE_TYPE, MERGE_REQUEST_NOTEABLE_TYPE, EPIC_NOTEABLE_TYPE } = constants;
|
|
|
|
|
|
|
|
if (state.noteableData.noteableType === EPIC_NOTEABLE_TYPE) {
|
|
|
|
return EPIC_NOTEABLE_TYPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return state.noteableData.merge_params ? MERGE_REQUEST_NOTEABLE_TYPE : ISSUE_NOTEABLE_TYPE;
|
|
|
|
};
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
const reverseNotes = array => array.slice(0).reverse();
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
const isLastNote = (note, state) =>
|
2018-11-08 19:23:39 +05:30
|
|
|
!note.system && state.userData && note.author && note.author.id === state.userData.id;
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
export const getCurrentUserLastNote = state =>
|
2020-03-13 15:44:24 +05:30
|
|
|
flattenDeep(reverseNotes(state.discussions).map(note => reverseNotes(note.notes))).find(el =>
|
2018-11-08 19:23:39 +05:30
|
|
|
isLastNote(el, state),
|
|
|
|
);
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
export const getDiscussionLastNote = state => discussion =>
|
|
|
|
reverseNotes(discussion.notes).find(el => isLastNote(el, state));
|
2018-03-27 19:54:05 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
export const unresolvedDiscussionsCount = state => state.unresolvedDiscussionsCount;
|
|
|
|
export const resolvableDiscussionsCount = state => state.resolvableDiscussionsCount;
|
2018-03-27 19:54:05 +05:30
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
export const showJumpToNextDiscussion = (state, getters) => (mode = 'discussion') => {
|
2019-02-15 15:39:39 +05:30
|
|
|
const orderedDiffs =
|
|
|
|
mode !== 'discussion'
|
|
|
|
? getters.unresolvedDiscussionsIdsByDiff
|
|
|
|
: getters.unresolvedDiscussionsIdsByDate;
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
return orderedDiffs.length > 1;
|
2018-03-27 19:54:05 +05:30
|
|
|
};
|
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
export const isDiscussionResolved = (state, getters) => discussionId =>
|
|
|
|
getters.resolvedDiscussionsById[discussionId] !== undefined;
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
export const allResolvableDiscussions = state =>
|
|
|
|
state.discussions.filter(d => !d.individual_note && d.resolvable);
|
2018-11-18 11:00:15 +05:30
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
export const resolvedDiscussionsById = state => {
|
2018-03-27 19:54:05 +05:30
|
|
|
const map = {};
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
state.discussions
|
|
|
|
.filter(d => d.resolvable)
|
|
|
|
.forEach(n => {
|
|
|
|
if (n.notes) {
|
|
|
|
const resolved = n.notes.filter(note => note.resolvable).every(note => note.resolved);
|
2018-03-27 19:54:05 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
if (resolved) {
|
|
|
|
map[n.id] = n;
|
|
|
|
}
|
2018-03-27 19:54:05 +05:30
|
|
|
}
|
2019-02-15 15:39:39 +05:30
|
|
|
});
|
2018-03-27 19:54:05 +05:30
|
|
|
|
|
|
|
return map;
|
|
|
|
};
|
|
|
|
|
2018-11-18 11:00:15 +05:30
|
|
|
// Gets Discussions IDs ordered by the date of their initial note
|
|
|
|
export const unresolvedDiscussionsIdsByDate = (state, getters) =>
|
|
|
|
getters.allResolvableDiscussions
|
|
|
|
.filter(d => !d.resolved)
|
|
|
|
.sort((a, b) => {
|
|
|
|
const aDate = new Date(a.notes[0].created_at);
|
|
|
|
const bDate = new Date(b.notes[0].created_at);
|
|
|
|
|
|
|
|
if (aDate < bDate) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return aDate === bDate ? 0 : 1;
|
|
|
|
})
|
|
|
|
.map(d => d.id);
|
|
|
|
|
|
|
|
// Gets Discussions IDs ordered by their position in the diff
|
|
|
|
//
|
|
|
|
// Sorts the array of resolvable yet unresolved discussions by
|
|
|
|
// comparing file names first. If file names are the same, compares
|
|
|
|
// line numbers.
|
|
|
|
export const unresolvedDiscussionsIdsByDiff = (state, getters) =>
|
|
|
|
getters.allResolvableDiscussions
|
2019-02-15 15:39:39 +05:30
|
|
|
.filter(d => !d.resolved && d.active)
|
2018-11-18 11:00:15 +05:30
|
|
|
.sort((a, b) => {
|
|
|
|
if (!a.diff_file || !b.diff_file) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get file names comparison result
|
|
|
|
const filenameComparison = a.diff_file.file_path.localeCompare(b.diff_file.file_path);
|
|
|
|
|
|
|
|
// Get the line numbers, to compare within the same file
|
2018-11-20 20:47:30 +05:30
|
|
|
const aLines = [a.position.new_line, a.position.old_line];
|
|
|
|
const bLines = [b.position.new_line, b.position.old_line];
|
2018-11-18 11:00:15 +05:30
|
|
|
|
|
|
|
return filenameComparison < 0 ||
|
|
|
|
(filenameComparison === 0 &&
|
|
|
|
// .max() because one of them might be zero (if removed/added)
|
|
|
|
Math.max(aLines[0], aLines[1]) < Math.max(bLines[0], bLines[1]))
|
|
|
|
? -1
|
|
|
|
: 1;
|
|
|
|
})
|
|
|
|
.map(d => d.id);
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
export const resolvedDiscussionCount = (state, getters) => {
|
|
|
|
const resolvedMap = getters.resolvedDiscussionsById;
|
|
|
|
|
|
|
|
return Object.keys(resolvedMap).length;
|
|
|
|
};
|
2018-10-15 14:42:47 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
export const discussionTabCounter = state =>
|
|
|
|
state.discussions.reduce(
|
|
|
|
(acc, discussion) =>
|
|
|
|
acc + discussion.notes.filter(note => !note.system && !note.placeholder).length,
|
|
|
|
0,
|
|
|
|
);
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2018-11-18 11:00:15 +05:30
|
|
|
// Returns the list of discussion IDs ordered according to given parameter
|
|
|
|
// @param {Boolean} diffOrder - is ordered by diff?
|
|
|
|
export const unresolvedDiscussionsIdsOrdered = (state, getters) => diffOrder => {
|
|
|
|
if (diffOrder) {
|
|
|
|
return getters.unresolvedDiscussionsIdsByDiff;
|
|
|
|
}
|
|
|
|
return getters.unresolvedDiscussionsIdsByDate;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Checks if a given discussion is the last in the current order (diff or date)
|
|
|
|
// @param {Boolean} discussionId - id of the discussion
|
|
|
|
// @param {Boolean} diffOrder - is ordered by diff?
|
|
|
|
export const isLastUnresolvedDiscussion = (state, getters) => (discussionId, diffOrder) => {
|
|
|
|
const idsOrdered = getters.unresolvedDiscussionsIdsOrdered(diffOrder);
|
|
|
|
const lastDiscussionId = idsOrdered[idsOrdered.length - 1];
|
|
|
|
|
|
|
|
return lastDiscussionId === discussionId;
|
|
|
|
};
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
export const findUnresolvedDiscussionIdNeighbor = (state, getters) => ({
|
|
|
|
discussionId,
|
|
|
|
diffOrder,
|
|
|
|
step,
|
|
|
|
}) => {
|
|
|
|
const ids = getters.unresolvedDiscussionsIdsOrdered(diffOrder);
|
|
|
|
const index = ids.indexOf(discussionId) + step;
|
|
|
|
|
|
|
|
if (index < 0 && step < 0) {
|
|
|
|
return ids[ids.length - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index === ids.length && step > 0) {
|
|
|
|
return ids[0];
|
|
|
|
}
|
2018-11-18 11:00:15 +05:30
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
return ids[index];
|
2018-11-18 11:00:15 +05:30
|
|
|
};
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
// Gets the ID of the discussion following the one provided, respecting order (diff or date)
|
|
|
|
// @param {Boolean} discussionId - id of the current discussion
|
|
|
|
// @param {Boolean} diffOrder - is ordered by diff?
|
|
|
|
export const nextUnresolvedDiscussionId = (state, getters) => (discussionId, diffOrder) =>
|
|
|
|
getters.findUnresolvedDiscussionIdNeighbor({ discussionId, diffOrder, step: 1 });
|
2019-10-12 21:52:04 +05:30
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
export const previousUnresolvedDiscussionId = (state, getters) => (discussionId, diffOrder) =>
|
|
|
|
getters.findUnresolvedDiscussionIdNeighbor({ discussionId, diffOrder, step: -1 });
|
2019-10-12 21:52:04 +05:30
|
|
|
|
2018-11-18 11:00:15 +05:30
|
|
|
// @param {Boolean} diffOrder - is ordered by diff?
|
|
|
|
export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => {
|
|
|
|
if (diffOrder) {
|
|
|
|
return getters.unresolvedDiscussionsIdsByDiff[0];
|
|
|
|
}
|
|
|
|
return getters.unresolvedDiscussionsIdsByDate[0];
|
|
|
|
};
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
export const getDiscussion = state => discussionId =>
|
|
|
|
state.discussions.find(discussion => discussion.id === discussionId);
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
export const commentsDisabled = state => state.commentsDisabled;
|
|
|
|
|
2018-10-15 14:42:47 +05:30
|
|
|
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
|
|
|
export default () => {};
|