focalboard/webapp/src/octoUtils.tsx
Doug Lauder 4feafb9806
Channels style UUID (#1369)
* server channels style uuids
* webapp channels style uuids
2021-10-05 09:52:59 -04:00

178 lines
7.5 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {IntlShape} from 'react-intl'
import {DateUtils} from 'react-day-picker'
import {Block, createBlock} from './blocks/block'
import {IPropertyTemplate, createBoard} from './blocks/board'
import {BoardView, createBoardView} from './blocks/boardView'
import {Card, createCard} from './blocks/card'
import {createCommentBlock} from './blocks/commentBlock'
import {createCheckboxBlock} from './blocks/checkboxBlock'
import {createDividerBlock} from './blocks/dividerBlock'
import {createImageBlock} from './blocks/imageBlock'
import {createTextBlock} from './blocks/textBlock'
import {FilterCondition} from './blocks/filterClause'
import {Utils} from './utils'
class OctoUtils {
static propertyDisplayValue(block: Block, propertyValue: string | string[] | undefined, propertyTemplate: IPropertyTemplate, intl: IntlShape): string | string[] | undefined {
let displayValue: string | string[] | undefined
switch (propertyTemplate.type) {
case 'select': {
// The property value is the id of the template
if (propertyValue) {
const option = propertyTemplate.options.find((o) => o.id === propertyValue)
if (!option) {
Utils.assertFailure(`Invalid select option ID ${propertyValue}, block.title: ${block.title}`)
}
displayValue = option?.value || '(Unknown)'
}
break
}
case 'multiSelect': {
if (propertyValue?.length) {
const options = propertyTemplate.options.filter((o) => propertyValue.includes(o.id))
if (!options.length) {
Utils.assertFailure(`Invalid multiSelect option IDs ${propertyValue}, block.title: ${block.title}`)
}
displayValue = options.map((o) => o.value)
}
break
}
case 'createdTime': {
displayValue = Utils.displayDateTime(new Date(block.createAt), intl)
break
}
case 'updatedTime': {
displayValue = Utils.displayDateTime(new Date(block.updateAt), intl)
break
}
case 'date': {
if (propertyValue) {
const singleDate = new Date(parseInt(propertyValue as string, 10))
if (singleDate && DateUtils.isDate(singleDate)) {
displayValue = Utils.displayDate(new Date(parseInt(propertyValue as string, 10)), intl)
} else {
try {
const dateValue = JSON.parse(propertyValue as string)
if (dateValue.from) {
displayValue = Utils.displayDate(new Date(dateValue.from), intl)
}
if (dateValue.to) {
displayValue += ' -> '
displayValue += Utils.displayDate(new Date(dateValue.to), intl)
}
} catch {
// do nothing
}
}
}
break
}
default:
displayValue = propertyValue
}
return displayValue
}
static hydrateBlock(block: Block): Block {
switch (block.type) {
case 'board': { return createBoard(block) }
case 'view': { return createBoardView(block) }
case 'card': { return createCard(block) }
case 'text': { return createTextBlock(block) }
case 'image': { return createImageBlock(block) }
case 'divider': { return createDividerBlock(block) }
case 'comment': { return createCommentBlock(block) }
case 'checkbox': { return createCheckboxBlock(block) }
default: {
Utils.assertFailure(`Can't hydrate unknown block type: ${block.type}`)
return createBlock(block)
}
}
}
static hydrateBlocks(blocks: readonly Block[]): Block[] {
return blocks.map((block) => this.hydrateBlock(block))
}
static mergeBlocks(blocks: readonly Block[], updatedBlocks: readonly Block[]): Block[] {
const updatedBlockIds = updatedBlocks.map((o) => o.id)
const newBlocks = blocks.filter((o) => !updatedBlockIds.includes(o.id))
const updatedAndNotDeletedBlocks = updatedBlocks.filter((o) => o.deleteAt === 0)
newBlocks.push(...updatedAndNotDeletedBlocks)
return newBlocks
}
// Creates a copy of the blocks with new ids and parentIDs
static duplicateBlockTree(blocks: readonly Block[], sourceBlockId: string): [Block[], Block, Readonly<Record<string, string>>] {
const idMap: Record<string, string> = {}
const now = Date.now()
const newBlocks = blocks.map((block) => {
const newBlock = this.hydrateBlock(block)
newBlock.id = Utils.createGuid(Utils.blockTypeToIDType(newBlock.type))
newBlock.createAt = now
newBlock.updateAt = now
idMap[block.id] = newBlock.id
return newBlock
})
const newSourceBlockId = idMap[sourceBlockId]
// Determine the new rootId if needed
let newRootId: string
const sourceBlock = blocks.find((block) => block.id === sourceBlockId)!
if (sourceBlock.rootId === sourceBlock.id) {
// Special case: when duplicating a tree from root, remap all the descendant rootIds
const newSourceRootBlock = newBlocks.find((block) => block.id === newSourceBlockId)!
newRootId = newSourceRootBlock.id
}
newBlocks.forEach((newBlock) => {
// Note: Don't remap the parent of the new root block
if (newBlock.id !== newSourceBlockId && newBlock.parentId) {
newBlock.parentId = idMap[newBlock.parentId] || newBlock.parentId
Utils.assert(newBlock.parentId, `Block ${newBlock.id} (${newBlock.type} ${newBlock.title}) has no parent`)
}
// Remap the rootIds if we are duplicating a tree from root
if (newRootId) {
newBlock.rootId = newRootId
}
// Remap manual card order
if (newBlock.type === 'view') {
const view = newBlock as BoardView
view.fields.cardOrder = view.fields.cardOrder.map((o) => idMap[o])
}
// Remap card content order
if (newBlock.type === 'card') {
const card = newBlock as Card
card.fields.contentOrder = card.fields.contentOrder.map((o) => (Array.isArray(o) ? o.map((o2) => idMap[o2]) : idMap[o]))
}
})
const newSourceBlock = newBlocks.find((block) => block.id === newSourceBlockId)!
return [newBlocks, newSourceBlock, idMap]
}
static filterConditionDisplayString(filterCondition: FilterCondition, intl: IntlShape): string {
switch (filterCondition) {
case 'includes': return intl.formatMessage({id: 'Filter.includes', defaultMessage: 'includes'})
case 'notIncludes': return intl.formatMessage({id: 'Filter.not-includes', defaultMessage: 'doesn\'t include'})
case 'isEmpty': return intl.formatMessage({id: 'Filter.is-empty', defaultMessage: 'is empty'})
case 'isNotEmpty': return intl.formatMessage({id: 'Filter.is-not-empty', defaultMessage: 'is not empty'})
default: {
Utils.assertFailure()
return '(unknown)'
}
}
}
}
export {OctoUtils}