wuffle
Version:
A multi-repository task board for GitHub issues
190 lines (134 loc) • 3.76 kB
JavaScript
const States = {
DEFAULT: 'DEFAULT',
IN_PROGRESS: 'IN_PROGRESS',
DONE: 'DONE',
IN_REVIEW: 'IN_REVIEW',
EXTERNAL_CONTRIBUTION: 'EXTERNAL_CONTRIBUTION'
};
const StateToNames = {
DEFAULT: 'Inbox',
IN_PROGRESS: 'In Progress',
DONE: 'Done',
IN_REVIEW: 'Needs Review',
EXTERNAL_CONTRIBUTION: 'Inbox'
};
/**
* @typedef { {
* name: string,
* label?: string|null,
* closed?: boolean,
* states?: string[]
* } } ColumnDefinition
*/
export default class Columns {
/**
* @param { ColumnDefinition[] } columns
*/
constructor(columns) {
this.columns = columns;
this.columnsByState = groupByStates(columns);
this.columnsByLabel = groupByLabel(columns);
this.columnsByName = groupByName(columns);
this._getter = createColumnGetter(columns);
}
getAll() {
return this.columns;
}
getIssueColumn(issue) {
return this._getter(issue);
}
getByName(columnName) {
return this.columnsByName[columnName];
}
getByState(state) {
if (state in States) {
const column = this.columnsByState[state];
if (!column) {
throw new Error('no column mapped to state <' + state + '>');
}
return column;
}
throw new Error('invalid state <' + state + '>');
}
isSorting(columnName) {
const column = this.getByName(columnName);
return column && column.sorting;
}
isFifo(columnName) {
const column = this.getByName(columnName);
return column && column.fifo;
}
isColumnLabel(label) {
return label in this.columnsByLabel;
}
}
Columns.$inject = [ 'config.columns' ];
// helpers //////////////////
function createColumnGetter(columns) {
const sortedColumns = columns.slice().sort((a, b) => {
if (a.label && !b.label) {
return -1;
}
return 1;
});
const defaultColumn = columns.find(c => c.type === States.DEFAULT) || columns[0];
return function(issue) {
const {
column: issueColumn
} = issue;
const columns = sortedColumns.filter(column => {
const issueClosed = issue.state === 'closed';
const columnLabel = column.label;
const columnClosed = column.closed === true;
if (issueClosed !== columnClosed) {
return false;
}
if (!columnLabel && defaultColumn !== column) {
return true;
}
return issue.labels.find(l => l.name === columnLabel);
});
// ensure that column is stable; it only changes
// if the given configuration doesn't match the
// existing column
return columns.find(c => c.name === issueColumn) || columns[0] || defaultColumn;
};
}
function groupByStates(columns) {
return Object.keys(States).reduce((columnsByState, state) => {
const defaultName = StateToNames[state];
const column = (
columns.find(c => c.states && c.states.includes(state)) ||
columns.find(c => c.name === defaultName)
);
if (!column) {
throw new Error('no column mapped to state ' + state + ' or called ' + StateToNames[state]);
}
columnsByState[state] = column;
return columnsByState;
}, {});
}
function groupByLabel(columns) {
return columns.reduce((columnsByLabel, column) => {
const { label } = column;
if (label) {
if (columnsByLabel[label]) {
throw new Error('label already mapped to column: ' + label);
}
columnsByLabel[label] = column;
}
return columnsByLabel;
}, {});
}
function groupByName(columns) {
return columns.reduce((columnsByName, column) => {
const { name } = column;
if (name) {
if (columnsByName[name]) {
throw new Error('name already used: ' + name);
}
columnsByName[name] = column;
}
return columnsByName;
}, {});
}