diff options
Diffstat (limited to 'app/assets/javascripts/boards/stores')
-rw-r--r-- | app/assets/javascripts/boards/stores/boards_store.js | 148 |
1 files changed, 147 insertions, 1 deletions
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index 6da1cca9628..8b737d1dab0 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -11,6 +11,7 @@ import { __ } from '~/locale'; import axios from '~/lib/utils/axios_utils'; import { mergeUrlParams } from '~/lib/utils/url_utility'; import eventHub from '../eventhub'; +import { ListType } from '../constants'; const boardsStore = { disabled: false, @@ -39,6 +40,7 @@ const boardsStore = { issue: {}, list: {}, }, + multiSelect: { list: [] }, setEndpoints({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId, recentBoardsEndpoint }) { const listsEndpointGenerate = `${listsEndpoint}/generate.json`; @@ -51,7 +53,6 @@ const boardsStore = { recentBoardsEndpoint: `${recentBoardsEndpoint}.json`, }; }, - create() { this.state.lists = []; this.filter.path = getUrlParamsArray().join('&'); @@ -134,6 +135,107 @@ const boardsStore = { Object.assign(this.moving, { list, issue }); }, + moveMultipleIssuesToList({ listFrom, listTo, issues, newIndex }) { + const issueTo = issues.map(issue => listTo.findIssue(issue.id)); + const issueLists = _.flatten(issues.map(issue => issue.getLists())); + const listLabels = issueLists.map(list => list.label); + + const hasMoveableIssues = _.compact(issueTo).length > 0; + + if (!hasMoveableIssues) { + // Check if target list assignee is already present in this issue + if ( + listTo.type === ListType.assignee && + listFrom.type === ListType.assignee && + issues.some(issue => issue.findAssignee(listTo.assignee)) + ) { + const targetIssues = issues.map(issue => listTo.findIssue(issue.id)); + targetIssues.forEach(targetIssue => targetIssue.removeAssignee(listFrom.assignee)); + } else if (listTo.type === 'milestone') { + const currentMilestones = issues.map(issue => issue.milestone); + const currentLists = this.state.lists + .filter(list => list.type === 'milestone' && list.id !== listTo.id) + .filter(list => + list.issues.some(listIssue => issues.some(issue => listIssue.id === issue.id)), + ); + + issues.forEach(issue => { + currentMilestones.forEach(milestone => { + issue.removeMilestone(milestone); + }); + }); + + issues.forEach(issue => { + issue.addMilestone(listTo.milestone); + }); + + currentLists.forEach(currentList => { + issues.forEach(issue => { + currentList.removeIssue(issue); + }); + }); + + listTo.addMultipleIssues(issues, listFrom, newIndex); + } else { + // Add to new lists issues if it doesn't already exist + listTo.addMultipleIssues(issues, listFrom, newIndex); + } + } else { + listTo.updateMultipleIssues(issues, listFrom); + issues.forEach(issue => { + issue.removeLabel(listFrom.label); + }); + } + + if (listTo.type === ListType.closed && listFrom.type !== ListType.backlog) { + issueLists.forEach(list => { + issues.forEach(issue => { + list.removeIssue(issue); + }); + }); + + issues.forEach(issue => { + issue.removeLabels(listLabels); + }); + } else if (listTo.type === ListType.backlog && listFrom.type === ListType.assignee) { + issues.forEach(issue => { + issue.removeAssignee(listFrom.assignee); + }); + issueLists.forEach(list => { + issues.forEach(issue => { + list.removeIssue(issue); + }); + }); + } else if (listTo.type === ListType.backlog && listFrom.type === ListType.milestone) { + issues.forEach(issue => { + issue.removeMilestone(listFrom.milestone); + }); + issueLists.forEach(list => { + issues.forEach(issue => { + list.removeIssue(issue); + }); + }); + } else if ( + this.shouldRemoveIssue(listFrom, listTo) && + this.issuesAreContiguous(listFrom, issues) + ) { + listFrom.removeMultipleIssues(issues); + } + }, + + issuesAreContiguous(list, issues) { + // When there's only 1 issue selected, we can return early. + if (issues.length === 1) return true; + + // Create list of ids for issues involved. + const listIssueIds = list.issues.map(issue => issue.id); + const movedIssueIds = issues.map(issue => issue.id); + + // Check if moved issue IDs is sub-array + // of source list issue IDs (i.e. contiguous selection). + return listIssueIds.join('|').includes(movedIssueIds.join('|')); + }, + moveIssueToList(listFrom, listTo, issue, newIndex) { const issueTo = listTo.findIssue(issue.id); const issueLists = issue.getLists(); @@ -195,6 +297,17 @@ const boardsStore = { list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId); }, + moveMultipleIssuesInList({ list, issues, oldIndicies, newIndex, idArray }) { + const beforeId = parseInt(idArray[newIndex - 1], 10) || null; + const afterId = parseInt(idArray[newIndex + issues.length], 10) || null; + list.moveMultipleIssues({ + issues, + oldIndicies, + newIndex, + moveBeforeId: beforeId, + moveAfterId: afterId, + }); + }, findList(key, val, type = 'label') { const filteredList = this.state.lists.filter(list => { const byType = type @@ -260,6 +373,10 @@ const boardsStore = { }`; }, + generateMultiDragPath(boardId) { + return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues/bulk_move`; + }, + all() { return axios.get(this.state.endpoints.listsEndpoint); }, @@ -309,6 +426,16 @@ const boardsStore = { }); }, + moveMultipleIssues({ ids, fromListId, toListId, moveBeforeId, moveAfterId }) { + return axios.put(this.generateMultiDragPath(this.state.endpoints.boardId), { + from_list_id: fromListId, + to_list_id: toListId, + move_before_id: moveBeforeId, + move_after_id: moveAfterId, + ids, + }); + }, + newIssue(id, issue) { return axios.post(this.generateIssuesPath(id), { issue, @@ -379,6 +506,25 @@ const boardsStore = { setCurrentBoard(board) { this.state.currentBoard = board; }, + + toggleMultiSelect(issue) { + const selectedIssueIds = this.multiSelect.list.map(issue => issue.id); + const index = selectedIssueIds.indexOf(issue.id); + + if (index === -1) { + this.multiSelect.list.push(issue); + return; + } + + this.multiSelect.list = [ + ...this.multiSelect.list.slice(0, index), + ...this.multiSelect.list.slice(index + 1), + ]; + }, + + clearMultiSelect() { + this.multiSelect.list = []; + }, }; BoardsStoreEE.initEESpecific(boardsStore); |