import { select, put } from 'redux-saga/effects'; import { fetchIssues, addLabel, removeLabel } from '../actions/issues'; import { fetchIssuesSaga } from './issues'; import { BUILD_KANBOARD_SUCCESS, buildKanboard, BUILD_KANBOARD_FAILURE } from '../actions/kanboards'; import { Project, Issue } from '../../types/gitea'; import { Board, BoardLane } from '../../types/board'; import { KanboardLane, Kanboard, KanboardCard } from '../../types/kanboard'; export function* moveCardSaga(action: any) { const { boardID, fromLaneID, fromPosition, toLaneID, toPosition, } = action; if (fromLaneID === toLaneID) return; const { board, kanboard} = yield select(state => { return { kanboard: state.kanboards.byID[boardID], board: state.boards.byID[boardID] } }); const toLane = kanboard.lanes[toLaneID]; const card = toLane.cards[toPosition]; if (!card) return; yield put(addLabel(card.project, card.issue.number, board.lanes[toLaneID].issueLabel)); yield put(removeLabel(card.project, card.issue.number, board.lanes[fromLaneID].issueLabel)); } export function* buildKanboardSaga(action: any) { const { board } = action; let kanboard; try { for (let p, i = 0; (p = board.projects[i]); i++) { const { project } = yield fetchIssues(p); yield fetchIssuesSaga({ project }); } const issues = yield select(state => state.issues); kanboard = createKanboard(board, issues); } catch(error) { yield put({ type: BUILD_KANBOARD_FAILURE, error }); return } yield put({ type: BUILD_KANBOARD_SUCCESS, kanboard }); } export function* refreshKanboardSaga(action: any) { const { project } = action; const boards = yield select(state => state.boards); const boardValues = Object.values(boards.byID); for (let b: any, i = 0; (b = boardValues[i]); i++) { const hasProject = b.projects.indexOf(project) !== -1; if (!hasProject) continue; yield put(buildKanboard(b)); } } function createCards(projects: Project[], issues: any, lane: BoardLane, rest: Set) { const cards: KanboardCard[] = projects.reduce((laneCards, p) => { const projectIssues = p in issues.byProject ? issues.byProject[p] : []; return projectIssues.reduce((projectCards: KanboardCard[], issue: any) => { const hasLabel = issue.labels.some((l: any) => l.name === lane.issueLabel); const card = getMemoizedKanboardCard(issue.id, issue.title, p, issue); if (hasLabel) { projectCards.push(card); rest.delete(card); } else { rest.add(card); } return projectCards; }, laneCards); }, []); return cards; } const kanboardCardMemo: {[key: string]: KanboardCard} = {}; function getMemoizedKanboardCard(id: number, title: string, project: Project, issue: Issue): KanboardCard { const key = `${project.id}-${issue.id}-${id}`; if (kanboardCardMemo.hasOwnProperty(key)) return kanboardCardMemo[key]; kanboardCardMemo[key] = { id, title, project, issue }; return kanboardCardMemo[key]; } function resetKandboarCardMemo() { Object.keys(kanboardCardMemo).forEach(k => delete kanboardCardMemo[k]); } function createKanboardLanes(board: Board, issues: any): KanboardLane[] { const lanes: KanboardLane[] = []; const rest = new Set(); resetKandboarCardMemo(); board.lanes.forEach((l: BoardLane, i: number) => { const cards = createCards(board.projects, issues, l, rest); lanes.push({ id: i, title: l.title, cards, }); }); // Assign remaining issues board.lanes.forEach((l: BoardLane, i: number) => { if (!l.collectRemainingIssues) return; lanes[i].cards.push(...Array.from(rest.values())); }); resetKandboarCardMemo(); return lanes; } function createKanboard(board: Board, issues: any) { if (!board) return null; const kanboard = { id: board.id, lanes: createKanboardLanes(board, issues), }; return kanboard; }