diff --git a/webapp/.eslintrc.json b/webapp/.eslintrc.json index 0bc27cdde..bc60529d8 100644 --- a/webapp/.eslintrc.json +++ b/webapp/.eslintrc.json @@ -119,6 +119,8 @@ "variables": false } ], + "no-useless-constructor": 0, + "@typescript-eslint/no-useless-constructor": 2, "react/jsx-filename-extension": 0 } }, diff --git a/webapp/src/mutator.ts b/webapp/src/mutator.ts index 1b8b3ac9e..50d351ef6 100644 --- a/webapp/src/mutator.ts +++ b/webapp/src/mutator.ts @@ -171,23 +171,18 @@ class Mutator { return } - if (index < 0) { - index = board.cardProperties.length - } - - if (!template) { - template = { - id: Utils.createGuid(), - name: 'New Property', - type: 'text', - options: [], - } + const newTemplate = template || { + id: Utils.createGuid(), + name: 'New Property', + type: 'text', + options: [], } const oldBlocks: IBlock[] = [board] const newBoard = new MutableBoard(board) - newBoard.cardProperties.splice(index, 0, template) + const startIndex = (index >= 0) ? index : board.cardProperties.length + newBoard.cardProperties.splice(startIndex, 0, newTemplate) const changedBlocks: IBlock[] = [newBoard] let description = 'add property' @@ -196,7 +191,7 @@ class Mutator { oldBlocks.push(activeView) const newActiveView = new MutableBoardView(activeView) - newActiveView.visiblePropertyIds.push(template.id) + newActiveView.visiblePropertyIds.push(newTemplate.id) changedBlocks.push(newActiveView) description = 'add column' @@ -339,7 +334,7 @@ class Mutator { } async changePropertyOptionValue(boardTree: BoardTree, propertyTemplate: IPropertyTemplate, option: IPropertyOption, value: string) { - const {board, cards} = boardTree + const {board} = boardTree const oldBlocks: IBlock[] = [board] @@ -491,8 +486,8 @@ class Mutator { async duplicateCard(cardId: string, description = 'duplicate card', afterRedo?: (newBoardId: string) => Promise, beforeUndo?: () => Promise): Promise<[IBlock[], string]> { const blocks = await octoClient.getSubtree(cardId, 2) - let [newBlocks, idMap] = OctoUtils.duplicateBlockTree(blocks, cardId) - newBlocks = newBlocks.filter((o) => o.type !== 'comment') + const [newBlocks1, idMap] = OctoUtils.duplicateBlockTree(blocks, cardId) + const newBlocks = newBlocks1.filter((o) => o.type !== 'comment') Utils.log(`duplicateCard: duplicating ${newBlocks.length} blocks`) const newCardId = idMap[cardId] const newCard = newBlocks.find((o) => o.id === newCardId)! @@ -510,8 +505,8 @@ class Mutator { async duplicateBoard(boardId: string, description = 'duplicate board', afterRedo?: (newBoardId: string) => Promise, beforeUndo?: () => Promise): Promise<[IBlock[], string]> { const blocks = await octoClient.getSubtree(boardId, 3) - let [newBlocks, idMap] = OctoUtils.duplicateBlockTree(blocks, boardId) - newBlocks = newBlocks.filter((o) => o.type !== 'comment') + const [newBlocks1, idMap] = OctoUtils.duplicateBlockTree(blocks, boardId) + const newBlocks = newBlocks1.filter((o) => o.type !== 'comment') Utils.log(`duplicateBoard: duplicating ${newBlocks.length} blocks`) const newBoardId = idMap[boardId] const newBoard = newBlocks.find((o) => o.id === newBoardId)! diff --git a/webapp/src/pages/boardPage.tsx b/webapp/src/pages/boardPage.tsx index cb557b355..61418a821 100644 --- a/webapp/src/pages/boardPage.tsx +++ b/webapp/src/pages/boardPage.tsx @@ -164,13 +164,10 @@ export default class BoardPage extends React.Component { await boardTree.sync() // Default to first view - if (!viewId) { - viewId = boardTree.views[0].id - } - - boardTree.setActiveView(viewId) + boardTree.setActiveView(viewId || boardTree.views[0].id) // TODO: Handle error (viewId not found) + this.setState({ boardTree, boardId, diff --git a/webapp/src/viewModel/boardTree.ts b/webapp/src/viewModel/boardTree.ts index 18ea0cd88..373d2a6ed 100644 --- a/webapp/src/viewModel/boardTree.ts +++ b/webapp/src/viewModel/boardTree.ts @@ -293,103 +293,96 @@ class MutableBoardTree implements BoardTree { return cards } const {sortOptions} = activeView - let sortedCards: Card[] = [] if (sortOptions.length < 1) { Utils.log('Manual sort') - sortedCards = cards.sort((a, b) => this.manualOrder(activeView, a, b)) - } else { - sortOptions.forEach((sortOption) => { - if (sortOption.propertyId === Constants.titleColumnId) { - Utils.log('Sort by title') - sortedCards = cards.sort((a, b) => { - let result = this.titleOrCreatedOrder(a, b) + return cards.sort((a, b) => this.manualOrder(activeView, a, b)) + } - if (sortOption.reversed) { - result = -result - } - return result - }) - } else { - const sortPropertyId = sortOption.propertyId - const template = board.cardProperties.find((o) => o.id === sortPropertyId) - if (!template) { - Utils.logError(`Missing template for property id: ${sortPropertyId}`) - return cards.slice() + let sortedCards = cards + for (const sortOption of sortOptions) { + if (sortOption.propertyId === Constants.titleColumnId) { + Utils.log('Sort by title') + sortedCards = sortedCards.sort((a, b) => { + const result = this.titleOrCreatedOrder(a, b) + return sortOption.reversed ? -result : result + }) + } else { + const sortPropertyId = sortOption.propertyId + const template = board.cardProperties.find((o) => o.id === sortPropertyId) + if (!template) { + Utils.logError(`Missing template for property id: ${sortPropertyId}`) + return sortedCards + } + Utils.log(`Sort by property: ${template?.name}`) + sortedCards = sortedCards.sort((a, b) => { + // Always put cards with no titles at the bottom, regardless of sort + if (!a.title || !b.title) { + return this.titleOrCreatedOrder(a, b) } - Utils.log(`Sort by property: ${template?.name}`) - sortedCards = cards.sort((a, b) => { - // Always put cards with no titles at the bottom, regardless of sort - if (!a.title || !b.title) { + + const aValue = a.properties[sortPropertyId] || '' + const bValue = b.properties[sortPropertyId] || '' + let result = 0 + if (template.type === 'select') { + // Always put empty values at the bottom + if (aValue && !bValue) { + return -1 + } + if (bValue && !aValue) { + return 1 + } + if (!aValue && !bValue) { return this.titleOrCreatedOrder(a, b) } - const aValue = a.properties[sortPropertyId] || '' - const bValue = b.properties[sortPropertyId] || '' - let result = 0 - if (template.type === 'select') { - // Always put empty values at the bottom - if (aValue && !bValue) { - return -1 - } - if (bValue && !aValue) { - return 1 - } - if (!aValue && !bValue) { - return this.titleOrCreatedOrder(a, b) - } + // Sort by the option order (not alphabetically by value) + const aOrder = template.options.findIndex((o) => o.id === aValue) + const bOrder = template.options.findIndex((o) => o.id === bValue) - // Sort by the option order (not alphabetically by value) - const aOrder = template.options.findIndex((o) => o.id === aValue) - const bOrder = template.options.findIndex((o) => o.id === bValue) - - result = aOrder - bOrder - } else if (template.type === 'number' || template.type === 'date') { - // Always put empty values at the bottom - if (aValue && !bValue) { - return -1 - } - if (bValue && !aValue) { - return 1 - } - if (!aValue && !bValue) { - return this.titleOrCreatedOrder(a, b) - } - - result = Number(aValue) - Number(bValue) - } else if (template.type === 'createdTime') { - result = a.createAt - b.createAt - } else if (template.type === 'updatedTime') { - result = a.updateAt - b.updateAt - } else { - // Text-based sort - - // Always put empty values at the bottom - if (aValue && !bValue) { - return -1 - } - if (bValue && !aValue) { - return 1 - } - if (!aValue && !bValue) { - return this.titleOrCreatedOrder(a, b) - } - - result = aValue.localeCompare(bValue) + result = aOrder - bOrder + } else if (template.type === 'number' || template.type === 'date') { + // Always put empty values at the bottom + if (aValue && !bValue) { + return -1 + } + if (bValue && !aValue) { + return 1 + } + if (!aValue && !bValue) { + return this.titleOrCreatedOrder(a, b) } - if (result === 0) { - // In case of "ties", use the title order - result = this.titleOrCreatedOrder(a, b) + result = Number(aValue) - Number(bValue) + } else if (template.type === 'createdTime') { + result = a.createAt - b.createAt + } else if (template.type === 'updatedTime') { + result = a.updateAt - b.updateAt + } else { + // Text-based sort + + // Always put empty values at the bottom + if (aValue && !bValue) { + return -1 + } + if (bValue && !aValue) { + return 1 + } + if (!aValue && !bValue) { + return this.titleOrCreatedOrder(a, b) } - if (sortOption.reversed) { - result = -result - } - return result - }) - } - }) + result = aValue.localeCompare(bValue) + } + + if (result === 0) { + // In case of "ties", use the title order + result = this.titleOrCreatedOrder(a, b) + } + + return sortOption.reversed ? -result : result + }) + } } return sortedCards