2019-12-04 20:38:33 +05:30
|
|
|
/* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow */
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2018-11-20 20:47:30 +05:30
|
|
|
import { __ } from '~/locale';
|
2019-09-04 21:01:54 +05:30
|
|
|
import ListLabel from './label';
|
|
|
|
import ListAssignee from './assignee';
|
2019-12-04 20:38:33 +05:30
|
|
|
import ListIssue from 'ee_else_ce/boards/models/issue';
|
2019-09-30 21:07:59 +05:30
|
|
|
import { urlParamsToObject } from '~/lib/utils/common_utils';
|
2019-12-21 20:55:43 +05:30
|
|
|
import flash from '~/flash';
|
2018-12-13 13:39:08 +05:30
|
|
|
import boardsStore from '../stores/boards_store';
|
2019-07-31 22:56:46 +05:30
|
|
|
import ListMilestone from './milestone';
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
const PER_PAGE = 20;
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
const TYPES = {
|
|
|
|
backlog: {
|
|
|
|
isPreset: true,
|
|
|
|
isExpandable: true,
|
|
|
|
isBlank: false,
|
|
|
|
},
|
|
|
|
closed: {
|
|
|
|
isPreset: true,
|
|
|
|
isExpandable: true,
|
|
|
|
isBlank: false,
|
|
|
|
},
|
|
|
|
blank: {
|
|
|
|
isPreset: true,
|
|
|
|
isExpandable: false,
|
|
|
|
isBlank: true,
|
|
|
|
},
|
2019-09-30 21:07:59 +05:30
|
|
|
default: {
|
|
|
|
// includes label, assignee, and milestone lists
|
|
|
|
isPreset: false,
|
|
|
|
isExpandable: true,
|
|
|
|
isBlank: false,
|
|
|
|
},
|
2018-11-08 19:23:39 +05:30
|
|
|
};
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
class List {
|
2018-11-08 19:23:39 +05:30
|
|
|
constructor(obj, defaultAvatar) {
|
2017-08-17 22:00:37 +05:30
|
|
|
this.id = obj.id;
|
|
|
|
this._uid = this.guid();
|
|
|
|
this.position = obj.position;
|
2018-11-20 20:47:30 +05:30
|
|
|
this.title = obj.list_type === 'backlog' ? __('Open') : obj.title;
|
2017-08-17 22:00:37 +05:30
|
|
|
this.type = obj.list_type;
|
2018-11-08 19:23:39 +05:30
|
|
|
|
|
|
|
const typeInfo = this.getTypeInfo(this.type);
|
2019-09-04 21:01:54 +05:30
|
|
|
this.preset = Boolean(typeInfo.isPreset);
|
|
|
|
this.isExpandable = Boolean(typeInfo.isExpandable);
|
2019-12-04 20:38:33 +05:30
|
|
|
this.isExpanded = !obj.collapsed;
|
2017-08-17 22:00:37 +05:30
|
|
|
this.page = 1;
|
|
|
|
this.loading = true;
|
|
|
|
this.loadingMore = false;
|
|
|
|
this.issues = [];
|
|
|
|
this.issuesSize = 0;
|
|
|
|
this.defaultAvatar = defaultAvatar;
|
|
|
|
|
|
|
|
if (obj.label) {
|
|
|
|
this.label = new ListLabel(obj.label);
|
2018-11-08 19:23:39 +05:30
|
|
|
} else if (obj.user) {
|
|
|
|
this.assignee = new ListAssignee(obj.user);
|
|
|
|
this.title = this.assignee.name;
|
2019-09-30 21:07:59 +05:30
|
|
|
} else if (IS_EE && obj.milestone) {
|
2019-07-31 22:56:46 +05:30
|
|
|
this.milestone = new ListMilestone(obj.milestone);
|
|
|
|
this.title = this.milestone.title;
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
if (!typeInfo.isBlank && this.id) {
|
2017-08-17 22:00:37 +05:30
|
|
|
this.getIssues().catch(() => {
|
|
|
|
// TODO: handle request error
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
guid() {
|
2018-11-08 19:23:39 +05:30
|
|
|
const s4 = () =>
|
|
|
|
Math.floor((1 + Math.random()) * 0x10000)
|
|
|
|
.toString(16)
|
|
|
|
.substring(1);
|
2017-08-17 22:00:37 +05:30
|
|
|
return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
save() {
|
2019-07-31 22:56:46 +05:30
|
|
|
const entity = this.label || this.assignee || this.milestone;
|
2018-11-08 19:23:39 +05:30
|
|
|
let entityType = '';
|
|
|
|
if (this.label) {
|
|
|
|
entityType = 'label_id';
|
2019-07-31 22:56:46 +05:30
|
|
|
} else if (this.assignee) {
|
2018-11-08 19:23:39 +05:30
|
|
|
entityType = 'assignee_id';
|
2019-09-30 21:07:59 +05:30
|
|
|
} else if (IS_EE && this.milestone) {
|
2019-07-31 22:56:46 +05:30
|
|
|
entityType = 'milestone_id';
|
2018-11-08 19:23:39 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
return gl.boardService
|
|
|
|
.createList(entity.id, entityType)
|
2018-03-17 18:26:18 +05:30
|
|
|
.then(res => res.data)
|
2018-11-08 19:23:39 +05:30
|
|
|
.then(data => {
|
2017-08-17 22:00:37 +05:30
|
|
|
this.id = data.id;
|
|
|
|
this.type = data.list_type;
|
|
|
|
this.position = data.position;
|
2019-07-31 22:56:46 +05:30
|
|
|
this.label = data.label;
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
return this.getIssues();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
destroy() {
|
2018-12-13 13:39:08 +05:30
|
|
|
const index = boardsStore.state.lists.indexOf(this);
|
|
|
|
boardsStore.state.lists.splice(index, 1);
|
|
|
|
boardsStore.updateNewListDropdown(this.id);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
gl.boardService.destroyList(this.id).catch(() => {
|
|
|
|
// TODO: handle request error
|
|
|
|
});
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
update() {
|
2019-12-04 20:38:33 +05:30
|
|
|
const collapsed = !this.isExpanded;
|
|
|
|
return gl.boardService.updateList(this.id, this.position, collapsed).catch(() => {
|
2018-11-08 19:23:39 +05:30
|
|
|
// TODO: handle request error
|
|
|
|
});
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
nextPage() {
|
2017-08-17 22:00:37 +05:30
|
|
|
if (this.issuesSize > this.issues.length) {
|
|
|
|
if (this.issues.length / PER_PAGE >= 1) {
|
|
|
|
this.page += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.getIssues(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
getIssues(emptyIssues = true) {
|
2018-11-20 20:47:30 +05:30
|
|
|
const data = {
|
2018-12-13 13:39:08 +05:30
|
|
|
...urlParamsToObject(boardsStore.filter.path),
|
2018-11-20 20:47:30 +05:30
|
|
|
page: this.page,
|
|
|
|
};
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
if (this.label && data.label_name) {
|
|
|
|
data.label_name = data.label_name.filter(label => label !== this.label.title);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (emptyIssues) {
|
|
|
|
this.loading = true;
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
return gl.boardService
|
|
|
|
.getIssuesForList(this.id, data)
|
2018-03-17 18:26:18 +05:30
|
|
|
.then(res => res.data)
|
2018-11-08 19:23:39 +05:30
|
|
|
.then(data => {
|
2017-08-17 22:00:37 +05:30
|
|
|
this.loading = false;
|
|
|
|
this.issuesSize = data.size;
|
|
|
|
|
|
|
|
if (emptyIssues) {
|
|
|
|
this.issues = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
this.createIssues(data.issues);
|
2018-11-18 11:00:15 +05:30
|
|
|
|
|
|
|
return data;
|
2017-08-17 22:00:37 +05:30
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
newIssue(issue) {
|
2017-09-10 17:25:29 +05:30
|
|
|
this.addIssue(issue, null, 0);
|
2017-08-17 22:00:37 +05:30
|
|
|
this.issuesSize += 1;
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
return gl.boardService
|
|
|
|
.newIssue(this.id, issue)
|
2018-03-17 18:26:18 +05:30
|
|
|
.then(res => res.data)
|
2018-11-08 19:23:39 +05:30
|
|
|
.then(data => this.onNewIssueResponse(issue, data));
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
createIssues(data) {
|
|
|
|
data.forEach(issueObj => {
|
2017-08-17 22:00:37 +05:30
|
|
|
this.addIssue(new ListIssue(issueObj, this.defaultAvatar));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
addMultipleIssues(issues, listFrom, newIndex) {
|
|
|
|
let moveBeforeId = null;
|
|
|
|
let moveAfterId = null;
|
|
|
|
|
|
|
|
const listHasIssues = issues.every(issue => this.findIssue(issue.id));
|
|
|
|
|
|
|
|
if (!listHasIssues) {
|
|
|
|
if (newIndex !== undefined) {
|
|
|
|
if (this.issues[newIndex - 1]) {
|
|
|
|
moveBeforeId = this.issues[newIndex - 1].id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.issues[newIndex]) {
|
|
|
|
moveAfterId = this.issues[newIndex].id;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.issues.splice(newIndex, 0, ...issues);
|
|
|
|
} else {
|
|
|
|
this.issues.push(...issues);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.label) {
|
|
|
|
issues.forEach(issue => issue.addLabel(this.label));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.assignee) {
|
|
|
|
if (listFrom && listFrom.type === 'assignee') {
|
|
|
|
issues.forEach(issue => issue.removeAssignee(listFrom.assignee));
|
|
|
|
}
|
|
|
|
issues.forEach(issue => issue.addAssignee(this.assignee));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_EE && this.milestone) {
|
|
|
|
if (listFrom && listFrom.type === 'milestone') {
|
|
|
|
issues.forEach(issue => issue.removeMilestone(listFrom.milestone));
|
|
|
|
}
|
|
|
|
issues.forEach(issue => issue.addMilestone(this.milestone));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (listFrom) {
|
|
|
|
this.issuesSize += issues.length;
|
|
|
|
|
|
|
|
this.updateMultipleIssues(issues, listFrom, moveBeforeId, moveAfterId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
addIssue(issue, listFrom, newIndex) {
|
2018-03-17 18:26:18 +05:30
|
|
|
let moveBeforeId = null;
|
|
|
|
let moveAfterId = null;
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
if (!this.findIssue(issue.id)) {
|
|
|
|
if (newIndex !== undefined) {
|
|
|
|
this.issues.splice(newIndex, 0, issue);
|
|
|
|
|
|
|
|
if (this.issues[newIndex - 1]) {
|
2018-03-17 18:26:18 +05:30
|
|
|
moveBeforeId = this.issues[newIndex - 1].id;
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
if (this.issues[newIndex + 1]) {
|
2018-03-17 18:26:18 +05:30
|
|
|
moveAfterId = this.issues[newIndex + 1].id;
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.issues.push(issue);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.label) {
|
|
|
|
issue.addLabel(this.label);
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
if (this.assignee) {
|
|
|
|
if (listFrom && listFrom.type === 'assignee') {
|
|
|
|
issue.removeAssignee(listFrom.assignee);
|
|
|
|
}
|
|
|
|
issue.addAssignee(this.assignee);
|
|
|
|
}
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
if (IS_EE && this.milestone) {
|
2019-07-31 22:56:46 +05:30
|
|
|
if (listFrom && listFrom.type === 'milestone') {
|
|
|
|
issue.removeMilestone(listFrom.milestone);
|
|
|
|
}
|
|
|
|
issue.addMilestone(this.milestone);
|
|
|
|
}
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
if (listFrom) {
|
|
|
|
this.issuesSize += 1;
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
this.updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId);
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
moveIssue(issue, oldIndex, newIndex, moveBeforeId, moveAfterId) {
|
2017-08-17 22:00:37 +05:30
|
|
|
this.issues.splice(oldIndex, 1);
|
|
|
|
this.issues.splice(newIndex, 0, issue);
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
gl.boardService.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId).catch(() => {
|
|
|
|
// TODO: handle request error
|
|
|
|
});
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
moveMultipleIssues({ issues, oldIndicies, newIndex, moveBeforeId, moveAfterId }) {
|
|
|
|
oldIndicies.reverse().forEach(index => {
|
|
|
|
this.issues.splice(index, 1);
|
|
|
|
});
|
|
|
|
this.issues.splice(newIndex, 0, ...issues);
|
|
|
|
|
|
|
|
gl.boardService
|
|
|
|
.moveMultipleIssues({
|
|
|
|
ids: issues.map(issue => issue.id),
|
|
|
|
fromListId: null,
|
|
|
|
toListId: null,
|
|
|
|
moveBeforeId,
|
|
|
|
moveAfterId,
|
|
|
|
})
|
|
|
|
.catch(() => flash(__('Something went wrong while moving issues.')));
|
|
|
|
}
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId) {
|
2018-11-08 19:23:39 +05:30
|
|
|
gl.boardService
|
|
|
|
.moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId)
|
2017-08-17 22:00:37 +05:30
|
|
|
.catch(() => {
|
|
|
|
// TODO: handle request error
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
updateMultipleIssues(issues, listFrom, moveBeforeId, moveAfterId) {
|
|
|
|
gl.boardService
|
|
|
|
.moveMultipleIssues({
|
|
|
|
ids: issues.map(issue => issue.id),
|
|
|
|
fromListId: listFrom.id,
|
|
|
|
toListId: this.id,
|
|
|
|
moveBeforeId,
|
|
|
|
moveAfterId,
|
|
|
|
})
|
|
|
|
.catch(() => flash(__('Something went wrong while moving issues.')));
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
findIssue(id) {
|
2018-03-17 18:26:18 +05:30
|
|
|
return this.issues.find(issue => issue.id === id);
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
removeMultipleIssues(removeIssues) {
|
|
|
|
const ids = removeIssues.map(issue => issue.id);
|
|
|
|
|
|
|
|
this.issues = this.issues.filter(issue => {
|
|
|
|
const matchesRemove = ids.includes(issue.id);
|
|
|
|
|
|
|
|
if (matchesRemove) {
|
|
|
|
this.issuesSize -= 1;
|
|
|
|
issue.removeLabel(this.label);
|
|
|
|
}
|
|
|
|
|
|
|
|
return !matchesRemove;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
removeIssue(removeIssue) {
|
|
|
|
this.issues = this.issues.filter(issue => {
|
2017-08-17 22:00:37 +05:30
|
|
|
const matchesRemove = removeIssue.id === issue.id;
|
|
|
|
|
|
|
|
if (matchesRemove) {
|
|
|
|
this.issuesSize -= 1;
|
|
|
|
issue.removeLabel(this.label);
|
|
|
|
}
|
|
|
|
|
|
|
|
return !matchesRemove;
|
|
|
|
});
|
|
|
|
}
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
getTypeInfo(type) {
|
2019-09-30 21:07:59 +05:30
|
|
|
return TYPES[type] || TYPES.default;
|
2018-11-08 19:23:39 +05:30
|
|
|
}
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
onNewIssueResponse(issue, data) {
|
2019-12-04 20:38:33 +05:30
|
|
|
issue.refreshData(data);
|
2018-11-08 19:23:39 +05:30
|
|
|
|
|
|
|
if (this.issuesSize > 1) {
|
|
|
|
const moveBeforeId = this.issues[1].id;
|
|
|
|
gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeId);
|
|
|
|
}
|
|
|
|
}
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
window.List = List;
|
2018-11-08 19:23:39 +05:30
|
|
|
|
|
|
|
export default List;
|